Sharing of lists via public links (#94)
This commit is contained in:
parent
88ea66798b
commit
8d57923a7d
41 changed files with 3425 additions and 590 deletions
|
@ -296,7 +296,7 @@ Sorry for some of them being in German, I'll tranlate them at some point.
|
||||||
* [ ] User online status
|
* [ ] User online status
|
||||||
* [ ] More sharing features (all of these with the already existing permissions)
|
* [ ] More sharing features (all of these with the already existing permissions)
|
||||||
* [ ] Invite users per mail
|
* [ ] Invite users per mail
|
||||||
* [ ] Share a link with/without password
|
* [x] Share a link with/without password
|
||||||
* [ ] Comments on tasks
|
* [ ] Comments on tasks
|
||||||
* [ ] @mention users in tasks or comments to get them notified
|
* [ ] @mention users in tasks or comments to get them notified
|
||||||
* [ ] Summary of tasks to do in a configurable interval (every day/week or so)
|
* [ ] Summary of tasks to do in a configurable interval (every day/week or so)
|
||||||
|
|
|
@ -18,6 +18,8 @@ service:
|
||||||
enablemetrics: false
|
enablemetrics: false
|
||||||
# Enable the caldav endpoint, see the docs for more details
|
# Enable the caldav endpoint, see the docs for more details
|
||||||
enablecaldav: true
|
enablecaldav: true
|
||||||
|
# Enable sharing of lists via a link
|
||||||
|
enablelinksharing: true
|
||||||
|
|
||||||
database:
|
database:
|
||||||
# Database type to use. Supported types are mysql and sqlite.
|
# Database type to use. Supported types are mysql and sqlite.
|
||||||
|
|
|
@ -61,6 +61,8 @@ service:
|
||||||
enablemetrics: false
|
enablemetrics: false
|
||||||
# Enable the caldav endpoint, see the docs for more details
|
# Enable the caldav endpoint, see the docs for more details
|
||||||
enablecaldav: true
|
enablecaldav: true
|
||||||
|
# Enable sharing of lists via a link
|
||||||
|
enablelinksharing: true
|
||||||
|
|
||||||
database:
|
database:
|
||||||
# Database type to use. Supported types are mysql and sqlite.
|
# Database type to use. Supported types are mysql and sqlite.
|
||||||
|
|
|
@ -14,6 +14,7 @@ This document describes the different errors Vikunja can return.
|
||||||
|
|
||||||
| ErrorCode | HTTP Status Code | Description |
|
| ErrorCode | HTTP Status Code | Description |
|
||||||
|-----------|------------------|-------------|
|
|-----------|------------------|-------------|
|
||||||
|
| 0001 | 403 | Generic forbidden error. |
|
||||||
| 1001 | 400 | A user with this username already exists. |
|
| 1001 | 400 | A user with this username already exists. |
|
||||||
| 1002 | 400 | A user with this email address already exists. |
|
| 1002 | 400 | A user with this email address already exists. |
|
||||||
| 1004 | 400 | No username and password specified. |
|
| 1004 | 400 | No username and password specified. |
|
||||||
|
@ -31,6 +32,7 @@ This document describes the different errors Vikunja can return.
|
||||||
| 3001 | 404 | The list does not exist. |
|
| 3001 | 404 | The list does not exist. |
|
||||||
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
|
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
|
||||||
| 3005 | 400 | The list title cannot be empty. |
|
| 3005 | 400 | The list title cannot be empty. |
|
||||||
|
| 3006 | 404 | The list share does not exist. |
|
||||||
| 4001 | 400 | The list task text cannot be empty. |
|
| 4001 | 400 | The list task text cannot be empty. |
|
||||||
| 4002 | 404 | The list task does not exist. |
|
| 4002 | 404 | The list task does not exist. |
|
||||||
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
|
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
|
||||||
|
|
|
@ -33,14 +33,15 @@ type Key string
|
||||||
|
|
||||||
// These constants hold all config value keys
|
// These constants hold all config value keys
|
||||||
const (
|
const (
|
||||||
ServiceJWTSecret Key = `service.JWTSecret`
|
ServiceJWTSecret Key = `service.JWTSecret`
|
||||||
ServiceInterface Key = `service.interface`
|
ServiceInterface Key = `service.interface`
|
||||||
ServiceFrontendurl Key = `service.frontendurl`
|
ServiceFrontendurl Key = `service.frontendurl`
|
||||||
ServiceEnableCaldav Key = `service.enablecaldav`
|
ServiceEnableCaldav Key = `service.enablecaldav`
|
||||||
ServiceRootpath Key = `service.rootpath`
|
ServiceRootpath Key = `service.rootpath`
|
||||||
ServicePageCount Key = `service.pagecount`
|
ServicePageCount Key = `service.pagecount`
|
||||||
ServiceEnableMetrics Key = `service.enablemetrics`
|
ServiceEnableMetrics Key = `service.enablemetrics`
|
||||||
ServiceMotd Key = `service.motd`
|
ServiceMotd Key = `service.motd`
|
||||||
|
ServiceEnableLinkSharing Key = `service.enablelinksharing`
|
||||||
|
|
||||||
DatabaseType Key = `database.type`
|
DatabaseType Key = `database.type`
|
||||||
DatabaseHost Key = `database.host`
|
DatabaseHost Key = `database.host`
|
||||||
|
@ -146,6 +147,7 @@ func InitConfig() {
|
||||||
ServicePageCount.setDefault(50)
|
ServicePageCount.setDefault(50)
|
||||||
ServiceEnableMetrics.setDefault(false)
|
ServiceEnableMetrics.setDefault(false)
|
||||||
ServiceMotd.setDefault("")
|
ServiceMotd.setDefault("")
|
||||||
|
ServiceEnableLinkSharing.setDefault(true)
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
DatabaseType.setDefault("sqlite")
|
DatabaseType.setDefault("sqlite")
|
||||||
|
|
|
@ -107,9 +107,9 @@ func newTestRequest(t *testing.T, method string, handler func(ctx echo.Context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTokenToContext(t *testing.T, user *models.User, c echo.Context) {
|
func addUserTokenToContext(t *testing.T, user *models.User, c echo.Context) {
|
||||||
// Get the token as a string
|
// Get the token as a string
|
||||||
token, err := v1.CreateNewJWTTokenForUser(user)
|
token, err := v1.NewUserJWTAuthtoken(user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// We send the string token through the parsing function to get a valid jwt.Token
|
// We send the string token through the parsing function to get a valid jwt.Token
|
||||||
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
@ -119,8 +119,20 @@ func addTokenToContext(t *testing.T, user *models.User, c echo.Context) {
|
||||||
c.Set("user", tken)
|
c.Set("user", tken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *models.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
func addLinkShareTokenToContext(t *testing.T, share *models.LinkSharing, c echo.Context) {
|
||||||
c, rec := bootstrapTestRequest(t, method, payload, queryParams)
|
// Get the token as a string
|
||||||
|
token, err := v1.NewLinkShareJWTAuthtoken(share)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// We send the string token through the parsing function to get a valid jwt.Token
|
||||||
|
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(config.ServiceJWTSecret.GetString()), nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
c.Set("user", tken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRequestSetup(t *testing.T, method string, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, c echo.Context) {
|
||||||
|
c, rec = bootstrapTestRequest(t, method, payload, queryParams)
|
||||||
|
|
||||||
var paramNames []string
|
var paramNames []string
|
||||||
var paramValues []string
|
var paramValues []string
|
||||||
|
@ -130,8 +142,19 @@ func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFun
|
||||||
}
|
}
|
||||||
c.SetParamNames(paramNames...)
|
c.SetParamNames(paramNames...)
|
||||||
c.SetParamValues(paramValues...)
|
c.SetParamValues(paramValues...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
addTokenToContext(t, user, c)
|
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *models.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||||
|
addUserTokenToContext(t, user, c)
|
||||||
|
err = handler(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestRequestWithLinkShare(t *testing.T, method string, handler echo.HandlerFunc, share *models.LinkSharing, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||||
|
addLinkShareTokenToContext(t, share, c)
|
||||||
err = handler(c)
|
err = handler(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -155,9 +178,10 @@ func assertHandlerErrorCode(t *testing.T, err error, expectedErrorCode int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type webHandlerTest struct {
|
type webHandlerTest struct {
|
||||||
user *models.User
|
user *models.User
|
||||||
strFunc func() handler.CObject
|
linkShare *models.LinkSharing
|
||||||
t *testing.T
|
strFunc func() handler.CObject
|
||||||
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) getHandler() handler.WebHandler {
|
func (h *webHandlerTest) getHandler() handler.WebHandler {
|
||||||
|
@ -168,27 +192,52 @@ func (h *webHandlerTest) getHandler() handler.WebHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) testReadAll(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
func (h *webHandlerTest) testReadAllWithUser(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
hndl := h.getHandler()
|
hndl := h.getHandler()
|
||||||
return newTestRequestWithUser(h.t, http.MethodGet, hndl.ReadAllWeb, h.user, "", queryParams, urlParams)
|
return newTestRequestWithUser(h.t, http.MethodGet, hndl.ReadAllWeb, h.user, "", queryParams, urlParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) testReadOne(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
func (h *webHandlerTest) testReadOneWithUser(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
hndl := h.getHandler()
|
hndl := h.getHandler()
|
||||||
return newTestRequestWithUser(h.t, http.MethodGet, hndl.ReadOneWeb, h.user, "", queryParams, urlParams)
|
return newTestRequestWithUser(h.t, http.MethodGet, hndl.ReadOneWeb, h.user, "", queryParams, urlParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) testCreate(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
func (h *webHandlerTest) testCreateWithUser(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
hndl := h.getHandler()
|
hndl := h.getHandler()
|
||||||
return newTestRequestWithUser(h.t, http.MethodPut, hndl.CreateWeb, h.user, payload, queryParams, urlParams)
|
return newTestRequestWithUser(h.t, http.MethodPut, hndl.CreateWeb, h.user, payload, queryParams, urlParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) testUpdate(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
func (h *webHandlerTest) testUpdateWithUser(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
hndl := h.getHandler()
|
hndl := h.getHandler()
|
||||||
return newTestRequestWithUser(h.t, http.MethodPost, hndl.UpdateWeb, h.user, payload, queryParams, urlParams)
|
return newTestRequestWithUser(h.t, http.MethodPost, hndl.UpdateWeb, h.user, payload, queryParams, urlParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *webHandlerTest) testDelete(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
func (h *webHandlerTest) testDeleteWithUser(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
hndl := h.getHandler()
|
hndl := h.getHandler()
|
||||||
return newTestRequestWithUser(h.t, http.MethodDelete, hndl.DeleteWeb, h.user, "", queryParams, urlParams)
|
return newTestRequestWithUser(h.t, http.MethodDelete, hndl.DeleteWeb, h.user, "", queryParams, urlParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *webHandlerTest) testReadAllWithLinkShare(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
hndl := h.getHandler()
|
||||||
|
return newTestRequestWithLinkShare(h.t, http.MethodGet, hndl.ReadAllWeb, h.linkShare, "", queryParams, urlParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *webHandlerTest) testReadOneWithLinkShare(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
hndl := h.getHandler()
|
||||||
|
return newTestRequestWithLinkShare(h.t, http.MethodGet, hndl.ReadOneWeb, h.linkShare, "", queryParams, urlParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *webHandlerTest) testCreateWithLinkShare(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
hndl := h.getHandler()
|
||||||
|
return newTestRequestWithLinkShare(h.t, http.MethodPut, hndl.CreateWeb, h.linkShare, payload, queryParams, urlParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *webHandlerTest) testUpdateWithLinkShare(queryParams url.Values, urlParams map[string]string, payload string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
hndl := h.getHandler()
|
||||||
|
return newTestRequestWithLinkShare(h.t, http.MethodPost, hndl.UpdateWeb, h.linkShare, payload, queryParams, urlParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *webHandlerTest) testDeleteWithLinkShare(queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||||
|
hndl := h.getHandler()
|
||||||
|
return newTestRequestWithLinkShare(h.t, http.MethodDelete, hndl.DeleteWeb, h.linkShare, "", queryParams, urlParams)
|
||||||
|
}
|
||||||
|
|
989
pkg/integrations/link_sharing_test.go
Normal file
989
pkg/integrations/link_sharing_test.go
Normal file
|
@ -0,0 +1,989 @@
|
||||||
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
|
// Copyright 2019 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/web/handler"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLinkSharing(t *testing.T) {
|
||||||
|
|
||||||
|
linkshareRead := &models.LinkSharing{
|
||||||
|
ID: 1,
|
||||||
|
Hash: "test1",
|
||||||
|
ListID: 1,
|
||||||
|
Right: models.RightRead,
|
||||||
|
SharingType: models.SharingTypeWithoutPassword,
|
||||||
|
SharedByID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
linkShareWrite := &models.LinkSharing{
|
||||||
|
ID: 2,
|
||||||
|
Hash: "test2",
|
||||||
|
ListID: 2,
|
||||||
|
Right: models.RightWrite,
|
||||||
|
SharingType: models.SharingTypeWithoutPassword,
|
||||||
|
SharedByID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
linkShareAdmin := &models.LinkSharing{
|
||||||
|
ID: 3,
|
||||||
|
Hash: "test3",
|
||||||
|
ListID: 3,
|
||||||
|
Right: models.RightAdmin,
|
||||||
|
SharingType: models.SharingTypeWithoutPassword,
|
||||||
|
SharedByID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Lists", func(t *testing.T) {
|
||||||
|
testHandlerListReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.List{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.List{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.List{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Normal", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListReadOnly.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Should only return the shared list, nothing else
|
||||||
|
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test3`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test5`)
|
||||||
|
})
|
||||||
|
t.Run("Search", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListReadOnly.testReadAllWithLinkShare(url.Values{"s": []string{"est1"}}, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Should only return the shared list, nothing else
|
||||||
|
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test3`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `Test5`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("ReadOne", func(t *testing.T) {
|
||||||
|
t.Run("Normal", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListReadOnly.testReadOneWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
|
||||||
|
})
|
||||||
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testReadOneWithLinkShare(nil, map[string]string{"list": "9999999"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
|
})
|
||||||
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
|
// List 2, not shared with this token
|
||||||
|
_, err := testHandlerListReadOnly.testReadOneWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `You don't have the right to see this`)
|
||||||
|
})
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListReadOnly.testReadOneWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListWrite.testReadOneWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"Test2"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListAdmin.testReadOneWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"Test3"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "9999999"}, `{"title":"TestLoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
|
})
|
||||||
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"title":"TestLoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"title":"TestLoremIpsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"title":"TestLoremIpsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testDeleteWithLinkShare(nil, map[string]string{"list": "9999999"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
|
})
|
||||||
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testDeleteWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testDeleteWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListWrite.testDeleteWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListAdmin.testDeleteWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Creating a list should always be forbidden, since users need access to a namespace to create a list
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "999999"}, `{"title":"Lorem"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListWrite.testCreateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListAdmin.testCreateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Right Management", func(t *testing.T) {
|
||||||
|
t.Run("Users", func(t *testing.T) {
|
||||||
|
testHandlerListUserReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.ListUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListUserWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.ListUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListUserAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.ListUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListUserReadOnly.testReadAllWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `[]`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListUserWrite.testReadAllWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `[]`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListUserAdmin.testReadAllWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"username":"user1"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserReadOnly.testDeleteWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserWrite.testDeleteWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListUserAdmin.testDeleteWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Teams", func(t *testing.T) {
|
||||||
|
testHandlerListTeamReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamList{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListTeamWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamList{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerListTeamAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamList{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListTeamReadOnly.testReadAllWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `[]`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListTeamWrite.testReadAllWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `[]`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerListTeamAdmin.testReadAllWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"name":"testteam1"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamReadOnly.testDeleteWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamWrite.testDeleteWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerListTeamAdmin.testDeleteWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Tasks", func(t *testing.T) {
|
||||||
|
testHandlerTaskReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Task{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerTaskWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Task{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerTaskAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Task{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskReadOnly.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #2`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #3`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #4`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #5`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #6`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #7`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #8`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #9`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #10`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #11`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #12`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskWrite.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #3`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #5`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #6`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #7`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #8`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #9`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskAdmin.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #5`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #6`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #7`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #8`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #9`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #32`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTaskReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTaskReadOnly.testUpdateWithLinkShare(nil, map[string]string{"listtask": "1"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskWrite.testUpdateWithLinkShare(nil, map[string]string{"listtask": "13"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskAdmin.testUpdateWithLinkShare(nil, map[string]string{"listtask": "32"}, `{"text":"Lorem Ipsum"}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTaskReadOnly.testDeleteWithLinkShare(nil, map[string]string{"listtask": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskWrite.testDeleteWithLinkShare(nil, map[string]string{"listtask": "13"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerTaskAdmin.testDeleteWithLinkShare(nil, map[string]string{"listtask": "32"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Teams", func(t *testing.T) {
|
||||||
|
testHandlerTeamReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Team{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerTeamWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Team{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerTeamAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Team{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamReadOnly.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamWrite.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamAdmin.testReadAllWithLinkShare(nil, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"team": "1"}, `{"name":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"team": "2"}, `{"name":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"team": "3"}, `{"name":"Lorem Ipsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamReadOnly.testDeleteWithLinkShare(nil, map[string]string{"team": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamWrite.testDeleteWithLinkShare(nil, map[string]string{"team": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerTeamAdmin.testDeleteWithLinkShare(nil, map[string]string{"team": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Linkshare Management", func(t *testing.T) {
|
||||||
|
testHandlerLinkShareReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.LinkSharing{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerLinkShareWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.LinkSharing{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerLinkShareAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.LinkSharing{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerLinkShareReadOnly.testReadAllWithLinkShare(nil, map[string]string{"list": "1"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"hash":"test"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerLinkShareWrite.testReadAllWithLinkShare(nil, map[string]string{"list": "2"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"hash":"test2"`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
rec, err := testHandlerLinkShareAdmin.testReadAllWithLinkShare(nil, map[string]string{"list": "3"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"hash":"test3"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareReadOnly.testUpdateWithLinkShare(nil, map[string]string{"share": "1"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareWrite.testUpdateWithLinkShare(nil, map[string]string{"share": "2"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareAdmin.testUpdateWithLinkShare(nil, map[string]string{"share": "3"}, `{}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareReadOnly.testDeleteWithLinkShare(nil, map[string]string{"share": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareWrite.testDeleteWithLinkShare(nil, map[string]string{"share": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerLinkShareAdmin.testDeleteWithLinkShare(nil, map[string]string{"share": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Namespace", func(t *testing.T) {
|
||||||
|
testHandlerNamespaceReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Namespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Namespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.Namespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceReadOnly.testReadAllWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceWrite.testReadAllWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceAdmin.testReadAllWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceReadOnly.testCreateWithLinkShare(nil, nil, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceWrite.testCreateWithLinkShare(nil, nil, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceAdmin.testCreateWithLinkShare(nil, nil, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"name":"LoremIpsum"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceReadOnly.testDeleteWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceWrite.testDeleteWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceAdmin.testDeleteWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Right Management", func(t *testing.T) {
|
||||||
|
t.Run("Users", func(t *testing.T) {
|
||||||
|
testHandlerNamespaceUserReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.NamespaceUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceUserWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.NamespaceUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceUserAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.NamespaceUser{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserReadOnly.testReadAllWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserWrite.testReadAllWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserAdmin.testReadAllWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserReadOnly.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserWrite.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserAdmin.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"userID":"user1"}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserReadOnly.testDeleteWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserWrite.testDeleteWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceUserAdmin.testDeleteWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Teams", func(t *testing.T) {
|
||||||
|
testHandlerNamespaceTeamReadOnly := webHandlerTest{
|
||||||
|
linkShare: linkshareRead,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamNamespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceTeamWrite := webHandlerTest{
|
||||||
|
linkShare: linkShareWrite,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamNamespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
testHandlerNamespaceTeamAdmin := webHandlerTest{
|
||||||
|
linkShare: linkShareAdmin,
|
||||||
|
strFunc: func() handler.CObject {
|
||||||
|
return &models.TeamNamespace{}
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamReadOnly.testReadAllWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamWrite.testReadAllWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamAdmin.testReadAllWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assertHandlerErrorCode(t, err, models.ErrCodeNeedToHaveNamespaceReadAccess)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamWrite.testCreateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Update", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"teamID":1}`)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Run("Shared readonly", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamReadOnly.testDeleteWithLinkShare(nil, map[string]string{"namespace": "1"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared write", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamWrite.testDeleteWithLinkShare(nil, map[string]string{"namespace": "2"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
t.Run("Shared admin", func(t *testing.T) {
|
||||||
|
_, err := testHandlerNamespaceTeamAdmin.testDeleteWithLinkShare(nil, map[string]string{"namespace": "3"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ func TestList(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Run("ReadAll", func(t *testing.T) {
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(nil, nil)
|
rec, err := testHandler.testReadAllWithUser(nil, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `Test2`)
|
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||||
|
@ -44,7 +44,7 @@ func TestList(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `Test5`)
|
assert.NotContains(t, rec.Body.String(), `Test5`)
|
||||||
})
|
})
|
||||||
t.Run("Search", func(t *testing.T) {
|
t.Run("Search", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"s": []string{"Test1"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"Test1"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `Test2`)
|
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||||
|
@ -55,7 +55,7 @@ func TestList(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("ReadOne", func(t *testing.T) {
|
t.Run("ReadOne", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "1"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "1"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
|
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
|
||||||
|
@ -64,77 +64,77 @@ func TestList(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"tasks":[{"id":1,"text":"task #1",`)
|
assert.Contains(t, rec.Body.String(), `"tasks":[{"id":1,"text":"task #1",`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testReadOne(nil, map[string]string{"list": "9999"})
|
_, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "9999"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
// Owned by user13
|
// Owned by user13
|
||||||
_, err := testHandler.testReadOne(nil, map[string]string{"list": "20"})
|
_, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "20"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `You don't have the right to see this`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `You don't have the right to see this`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "6"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "6"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test6"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test6"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "7"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "7"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test7"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test7"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "8"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "8"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test8"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test8"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "9"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "9"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test9"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test9"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "10"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "10"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test10"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test10"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "11"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "11"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test11"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test11"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "12"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "12"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test12"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test12"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "13"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "13"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test13"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test13"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "14"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "14"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test14"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test14"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "15"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "15"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test15"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test15"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "16"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "16"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test16"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test16"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadOne(nil, map[string]string{"list": "17"})
|
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"list": "17"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Test17"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Test17"`)
|
||||||
})
|
})
|
||||||
|
@ -142,106 +142,106 @@ func TestList(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Update", func(t *testing.T) {
|
t.Run("Update", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
// Check the list was loaded successfully afterwards, see testReadOne
|
// Check the list was loaded successfully afterwards, see testReadOneWithUser
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "1"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
// The description should not be updated but returned correctly
|
// The description should not be updated but returned correctly
|
||||||
assert.Contains(t, rec.Body.String(), `description":"Lorem Ipsum`)
|
assert.Contains(t, rec.Body.String(), `description":"Lorem Ipsum`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "9999"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "9999"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Normal with updating the description", func(t *testing.T) {
|
t.Run("Normal with updating the description", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "1"}, `{"title":"TestLoremIpsum","description":"Lorem Ipsum dolor sit amet"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "1"}, `{"title":"TestLoremIpsum","description":"Lorem Ipsum dolor sit amet"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum dolor sit amet`)
|
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum dolor sit amet`)
|
||||||
})
|
})
|
||||||
t.Run("Empty title", func(t *testing.T) {
|
t.Run("Empty title", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "1"}, `{"title":""}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "1"}, `{"title":""}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||||
})
|
})
|
||||||
t.Run("Almost empty title", func(t *testing.T) {
|
t.Run("Almost empty title", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "1"}, `{"title":"nn"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "1"}, `{"title":"nn"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
||||||
})
|
})
|
||||||
t.Run("Title too long", func(t *testing.T) {
|
t.Run("Title too long", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "1"}, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "1"}, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
// Owned by user13
|
// Owned by user13
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "20"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "20"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "6"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "6"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "7"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "7"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "8"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "8"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "9"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "9"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "10"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "10"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "11"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "11"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "12"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "12"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "13"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "13"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "14"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "14"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"list": "15"}, `{"title":"TestLoremIpsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "15"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "16"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "16"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"list": "17"}, `{"title":"TestLoremIpsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"list": "17"}, `{"title":"TestLoremIpsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||||
})
|
})
|
||||||
|
@ -249,82 +249,82 @@ func TestList(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"list": "1"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "1"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "999"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "999"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
// Owned by user13
|
// Owned by user13
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "20"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "20"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "6"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "6"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "7"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "7"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"list": "8"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "8"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "9"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "9"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "10"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "10"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"list": "11"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "11"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "12"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "12"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "13"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "13"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"list": "14"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "14"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "15"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "15"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"list": "16"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "16"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"list": "17"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"list": "17"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||||
})
|
})
|
||||||
|
@ -332,8 +332,8 @@ func TestList(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Create", func(t *testing.T) {
|
t.Run("Create", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
// Check the list was loaded successfully after update, see testReadOne
|
// Check the list was loaded successfully after update, see testReadOneWithUser
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
|
@ -341,7 +341,7 @@ func TestList(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
||||||
})
|
})
|
||||||
t.Run("Normal with description", func(t *testing.T) {
|
t.Run("Normal with description", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||||
|
@ -349,22 +349,22 @@ func TestList(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting Namespace", func(t *testing.T) {
|
t.Run("Nonexisting Namespace", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "999999"}, `{"title":"Lorem"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "999999"}, `{"title":"Lorem"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeNamespaceDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeNamespaceDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Empty title", func(t *testing.T) {
|
t.Run("Empty title", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "1"}, `{"title":""}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "1"}, `{"title":""}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||||
})
|
})
|
||||||
t.Run("Almost empty title", func(t *testing.T) {
|
t.Run("Almost empty title", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "1"}, `{"title":"nn"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "1"}, `{"title":"nn"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
||||||
})
|
})
|
||||||
t.Run("Title too long", func(t *testing.T) {
|
t.Run("Title too long", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(3|250)")
|
||||||
})
|
})
|
||||||
|
@ -372,18 +372,18 @@ func TestList(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
// Owned by user13
|
// Owned by user13
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "15"}, `{"title":"Lorem"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "15"}, `{"title":"Lorem"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "7"}, `{"title":"Lorem"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "7"}, `{"title":"Lorem"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "8"}, `{"title":"Lorem"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "8"}, `{"title":"Lorem"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
|
@ -391,7 +391,7 @@ func TestList(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "9"}, `{"title":"Lorem"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "9"}, `{"title":"Lorem"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
|
@ -400,12 +400,12 @@ func TestList(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"namespace": "10"}, `{"title":"Lorem"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "10"}, `{"title":"Lorem"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "11"}, `{"title":"Lorem"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "11"}, `{"title":"Lorem"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
|
@ -413,7 +413,7 @@ func TestList(t *testing.T) {
|
||||||
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
assert.Contains(t, rec.Body.String(), `"tasks":null`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"namespace": "12"}, `{"title":"Lorem"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "12"}, `{"title":"Lorem"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func TestTask(t *testing.T) {
|
||||||
// ^TestTask$/^Update$/^Update_task_items$/^Removing_Assignees_null$
|
// ^TestTask$/^Update$/^Update_task_items$/^Removing_Assignees_null$
|
||||||
t.Run("ReadAll", func(t *testing.T) {
|
t.Run("ReadAll", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(nil, nil)
|
rec, err := testHandler.testReadAllWithUser(nil, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
||||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||||
|
@ -58,7 +58,7 @@ func TestTask(t *testing.T) {
|
||||||
// has at least read access
|
// has at least read access
|
||||||
})
|
})
|
||||||
t.Run("Search", func(t *testing.T) {
|
t.Run("Search", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"s": []string{"task #6"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
@ -78,39 +78,39 @@ func TestTask(t *testing.T) {
|
||||||
t.Run("Sort Order", func(t *testing.T) {
|
t.Run("Sort Order", func(t *testing.T) {
|
||||||
// should equal priority desc
|
// should equal priority desc
|
||||||
t.Run("by priority", func(t *testing.T) {
|
t.Run("by priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"priority"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"priority"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
t.Run("by priority desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"prioritydesc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"prioritydesc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority asc", func(t *testing.T) {
|
t.Run("by priority asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"priorityasc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"priorityasc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":31,"text":"task #31 with color","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"f0f0f0","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":32,"text":"task #32","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":3,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate desc
|
// should equal duedate desc
|
||||||
t.Run("by duedate", func(t *testing.T) {
|
t.Run("by duedate", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"duedate"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedate"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
t.Run("by duedate desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"duedatedesc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedatedesc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
t.Run("by duedate asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"duedateasc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedateasc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":0,"dueDate":1543616724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`)
|
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":0,"dueDate":1543616724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"parentTaskID":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`)
|
||||||
})
|
})
|
||||||
t.Run("invalid parameter", func(t *testing.T) {
|
t.Run("invalid parameter", func(t *testing.T) {
|
||||||
// Invalid parameter should not sort at all
|
// Invalid parameter should not sort at all
|
||||||
rec, err := testHandler.testReadAll(url.Values{"sort": []string{"loremipsum"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"parentTaskID":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"subtasks":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||||
|
@ -120,7 +120,7 @@ func TestTask(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Date range", func(t *testing.T) {
|
t.Run("Date range", func(t *testing.T) {
|
||||||
t.Run("start and end date", func(t *testing.T) {
|
t.Run("start and end date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"startdate": []string{"1540000000"}, "enddate": []string{"1544700001"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"startdate": []string{"1540000000"}, "enddate": []string{"1544700001"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
@ -138,7 +138,7 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
})
|
})
|
||||||
t.Run("start date only", func(t *testing.T) {
|
t.Run("start date only", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"startdate": []string{"1540000000"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"startdate": []string{"1540000000"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
@ -156,7 +156,7 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
})
|
})
|
||||||
t.Run("end date only", func(t *testing.T) {
|
t.Run("end date only", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAll(url.Values{"enddate": []string{"1544700001"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"enddate": []string{"1544700001"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// If no start date but an end date is specified, this should be null
|
// If no start date but an end date is specified, this should be null
|
||||||
// since we don't have any tasks in the fixtures with an end date >
|
// since we don't have any tasks in the fixtures with an end date >
|
||||||
|
@ -168,162 +168,162 @@ func TestTask(t *testing.T) {
|
||||||
t.Run("Update", func(t *testing.T) {
|
t.Run("Update", func(t *testing.T) {
|
||||||
t.Run("Update task items", func(t *testing.T) {
|
t.Run("Update task items", func(t *testing.T) {
|
||||||
t.Run("Text", func(t *testing.T) {
|
t.Run("Text", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"text":"task #1"`)
|
assert.NotContains(t, rec.Body.String(), `"text":"task #1"`)
|
||||||
})
|
})
|
||||||
t.Run("Description", func(t *testing.T) {
|
t.Run("Description", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"description":"Dolor sit amet"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"description":"Dolor sit amet"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":"Dolor sit amet"`)
|
assert.Contains(t, rec.Body.String(), `"description":"Dolor sit amet"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Description to empty", func(t *testing.T) {
|
t.Run("Description to empty", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"description":""}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"description":""}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Done", func(t *testing.T) {
|
t.Run("Done", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"done":true}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"done":true}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"done":true`)
|
assert.Contains(t, rec.Body.String(), `"done":true`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"done":false`)
|
assert.NotContains(t, rec.Body.String(), `"done":false`)
|
||||||
})
|
})
|
||||||
t.Run("Undone", func(t *testing.T) {
|
t.Run("Undone", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "2"}, `{"done":false}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "2"}, `{"done":false}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"done":false`)
|
assert.Contains(t, rec.Body.String(), `"done":false`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
||||||
})
|
})
|
||||||
t.Run("Due date", func(t *testing.T) {
|
t.Run("Due date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"dueDate": 123456}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"dueDate": 123456}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"dueDate":123456`)
|
assert.Contains(t, rec.Body.String(), `"dueDate":123456`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"dueDate":0`)
|
assert.NotContains(t, rec.Body.String(), `"dueDate":0`)
|
||||||
})
|
})
|
||||||
t.Run("Due date unset", func(t *testing.T) {
|
t.Run("Due date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "5"}, `{"dueDate": 0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "5"}, `{"dueDate": 0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"dueDate":0`)
|
assert.Contains(t, rec.Body.String(), `"dueDate":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"dueDate":1543636724`)
|
assert.NotContains(t, rec.Body.String(), `"dueDate":1543636724`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders", func(t *testing.T) {
|
t.Run("Reminders", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"reminderDates": [1555508227,1555511000]}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"reminderDates": [1555508227,1555511000]}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminderDates":[1555508227,1555511000]`)
|
assert.Contains(t, rec.Body.String(), `"reminderDates":[1555508227,1555511000]`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminderDates": null`)
|
assert.NotContains(t, rec.Body.String(), `"reminderDates": null`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "27"}, `{"reminderDates": []}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminderDates": []}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders unset to null", func(t *testing.T) {
|
t.Run("Reminders unset to null", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "27"}, `{"reminderDates": null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminderDates": null}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
||||||
})
|
})
|
||||||
t.Run("Repeat after", func(t *testing.T) {
|
t.Run("Repeat after", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"repeatAfter":3600}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"repeatAfter":3600}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"repeatAfter":3600`)
|
assert.Contains(t, rec.Body.String(), `"repeatAfter":3600`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"repeatAfter":0`)
|
assert.NotContains(t, rec.Body.String(), `"repeatAfter":0`)
|
||||||
})
|
})
|
||||||
t.Run("Repeat after unset", func(t *testing.T) {
|
t.Run("Repeat after unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "28"}, `{"repeatAfter":0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "28"}, `{"repeatAfter":0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"repeatAfter":0`)
|
assert.Contains(t, rec.Body.String(), `"repeatAfter":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"repeatAfter":3600`)
|
assert.NotContains(t, rec.Body.String(), `"repeatAfter":3600`)
|
||||||
})
|
})
|
||||||
t.Run("Repeat after update done", func(t *testing.T) {
|
t.Run("Repeat after update done", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "28"}, `{"done":true}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "28"}, `{"done":true}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"done":false`)
|
assert.Contains(t, rec.Body.String(), `"done":false`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
||||||
})
|
})
|
||||||
t.Run("Parent task", func(t *testing.T) {
|
t.Run("Parent task", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"parentTaskID":2}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"parentTaskID":2}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"parentTaskID":2`)
|
assert.Contains(t, rec.Body.String(), `"parentTaskID":2`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"parentTaskID":0`)
|
assert.NotContains(t, rec.Body.String(), `"parentTaskID":0`)
|
||||||
})
|
})
|
||||||
t.Run("Parent task same task", func(t *testing.T) {
|
t.Run("Parent task same task", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"parentTaskID":1}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"parentTaskID":1}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeParentTaskCannotBeTheSame)
|
assertHandlerErrorCode(t, err, models.ErrCodeParentTaskCannotBeTheSame)
|
||||||
})
|
})
|
||||||
t.Run("Parent task unset", func(t *testing.T) {
|
t.Run("Parent task unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "29"}, `{"parentTaskID":0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "29"}, `{"parentTaskID":0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"parentTaskID":0`)
|
assert.Contains(t, rec.Body.String(), `"parentTaskID":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"parentTaskID":1`)
|
assert.NotContains(t, rec.Body.String(), `"parentTaskID":1`)
|
||||||
})
|
})
|
||||||
t.Run("Assignees", func(t *testing.T) {
|
t.Run("Assignees", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"assignees":[{"id":1}]}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"assignees":[{"id":1}]}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
assert.Contains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"assignees":[]`)
|
assert.NotContains(t, rec.Body.String(), `"assignees":[]`)
|
||||||
})
|
})
|
||||||
t.Run("Removing Assignees empty array", func(t *testing.T) {
|
t.Run("Removing Assignees empty array", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "30"}, `{"assignees":[]}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "30"}, `{"assignees":[]}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||||
})
|
})
|
||||||
t.Run("Removing Assignees null", func(t *testing.T) {
|
t.Run("Removing Assignees null", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "30"}, `{"assignees":null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "30"}, `{"assignees":null}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||||
})
|
})
|
||||||
t.Run("Priority", func(t *testing.T) {
|
t.Run("Priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"priority":100}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"priority":100}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"priority":100`)
|
assert.Contains(t, rec.Body.String(), `"priority":100`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"priority":0`)
|
assert.NotContains(t, rec.Body.String(), `"priority":0`)
|
||||||
})
|
})
|
||||||
t.Run("Priority to 0", func(t *testing.T) {
|
t.Run("Priority to 0", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "3"}, `{"priority":0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "3"}, `{"priority":0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"priority":0`)
|
assert.Contains(t, rec.Body.String(), `"priority":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"priority":100`)
|
assert.NotContains(t, rec.Body.String(), `"priority":100`)
|
||||||
})
|
})
|
||||||
t.Run("Start date", func(t *testing.T) {
|
t.Run("Start date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"startDate":1234567}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"startDate":1234567}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"startDate":1234567`)
|
assert.Contains(t, rec.Body.String(), `"startDate":1234567`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"startDate":0`)
|
assert.NotContains(t, rec.Body.String(), `"startDate":0`)
|
||||||
})
|
})
|
||||||
t.Run("Start date unset", func(t *testing.T) {
|
t.Run("Start date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "7"}, `{"startDate":0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "7"}, `{"startDate":0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"startDate":0`)
|
assert.Contains(t, rec.Body.String(), `"startDate":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"startDate":1544600000`)
|
assert.NotContains(t, rec.Body.String(), `"startDate":1544600000`)
|
||||||
})
|
})
|
||||||
t.Run("End date", func(t *testing.T) {
|
t.Run("End date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"endDate":123456}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"endDate":123456}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"endDate":123456`)
|
assert.Contains(t, rec.Body.String(), `"endDate":123456`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"endDate":0`)
|
assert.NotContains(t, rec.Body.String(), `"endDate":0`)
|
||||||
})
|
})
|
||||||
t.Run("End date unset", func(t *testing.T) {
|
t.Run("End date unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "8"}, `{"endDate":0}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "8"}, `{"endDate":0}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"endDate":0`)
|
assert.Contains(t, rec.Body.String(), `"endDate":0`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"endDate":1544700000`)
|
assert.NotContains(t, rec.Body.String(), `"endDate":1544700000`)
|
||||||
})
|
})
|
||||||
t.Run("Color", func(t *testing.T) {
|
t.Run("Color", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "1"}, `{"hexColor":"f0f0f0"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"hexColor":"f0f0f0"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
assert.Contains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"hexColor":""`)
|
assert.NotContains(t, rec.Body.String(), `"hexColor":""`)
|
||||||
})
|
})
|
||||||
t.Run("Color unset", func(t *testing.T) {
|
t.Run("Color unset", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "31"}, `{"hexColor":""}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "31"}, `{"hexColor":""}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"hexColor":""`)
|
assert.Contains(t, rec.Body.String(), `"hexColor":""`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
assert.NotContains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
||||||
|
@ -331,76 +331,76 @@ func TestTask(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "99999"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "99999"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "14"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "14"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "15"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "15"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "16"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "16"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "17"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "17"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "18"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "18"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "19"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "19"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "20"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "20"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "21"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "21"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "22"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "22"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "23"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "23"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testUpdate(nil, map[string]string{"listtask": "24"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "24"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "25"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "25"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdate(nil, map[string]string{"listtask": "26"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "26"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
@ -408,81 +408,81 @@ func TestTask(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "1"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "1"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "99999"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "99999"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "14"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "14"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "15"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "15"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "16"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "16"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "17"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "17"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "18"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "18"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "19"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "19"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "20"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "20"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "21"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "21"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "22"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "22"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "23"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "23"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testDelete(nil, map[string]string{"listtask": "24"})
|
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "24"})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "25"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "25"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testDelete(nil, map[string]string{"listtask": "26"})
|
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"listtask": "26"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||||
})
|
})
|
||||||
|
@ -490,82 +490,82 @@ func TestTask(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Create", func(t *testing.T) {
|
t.Run("Create", func(t *testing.T) {
|
||||||
t.Run("Normal", func(t *testing.T) {
|
t.Run("Normal", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Nonexisting", func(t *testing.T) {
|
t.Run("Nonexisting", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "9999"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "9999"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||||
})
|
})
|
||||||
t.Run("Rights check", func(t *testing.T) {
|
t.Run("Rights check", func(t *testing.T) {
|
||||||
t.Run("Forbidden", func(t *testing.T) {
|
t.Run("Forbidden", func(t *testing.T) {
|
||||||
// Owned by user13
|
// Owned by user13
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "20"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "20"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "6"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "6"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "7"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "7"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "8"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "8"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "9"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "9"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User write", func(t *testing.T) {
|
t.Run("Shared Via User write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "10"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "10"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "11"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "11"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "12"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "12"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "13"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "13"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "14"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "14"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||||
_, err := testHandler.testCreate(nil, map[string]string{"list": "15"}, `{"text":"Lorem Ipsum"}`)
|
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "15"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "16"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "16"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||||
rec, err := testHandler.testCreate(nil, map[string]string{"list": "17"}, `{"text":"Lorem Ipsum"}`)
|
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "17"}, `{"text":"Lorem Ipsum"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||||
})
|
})
|
||||||
|
|
53
pkg/migration/20190818210133.go
Normal file
53
pkg/migration/20190818210133.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2019 Vikunja and contriubtors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of Vikunja.
|
||||||
|
//
|
||||||
|
// Vikunja is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Vikunja is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
"src.techknowlogick.com/xormigrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linkSharing20190818210133 struct {
|
||||||
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
|
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
|
||||||
|
ListID int64 `xorm:"int(11) not null" json:"list_id"`
|
||||||
|
Right models.Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
SharingType models.SharingType `xorm:"int(11) INDEX not null default 0" json:"sharing_type" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
SharedByID int64 `xorm:"int(11) INDEX not null"`
|
||||||
|
Created int64 `xorm:"created not null" json:"created"`
|
||||||
|
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName holds the table name for this share
|
||||||
|
func (linkSharing20190818210133) TableName() string {
|
||||||
|
return "link_sharing"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations = append(migrations, &xormigrate.Migration{
|
||||||
|
ID: "20190818210133",
|
||||||
|
Description: "Add link sharing table",
|
||||||
|
Migrate: func(tx *xorm.Engine) error {
|
||||||
|
return tx.Sync2(linkSharing20190818210133{})
|
||||||
|
},
|
||||||
|
Rollback: func(tx *xorm.Engine) error {
|
||||||
|
return tx.DropTables(linkSharing20190818210133{})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -22,6 +22,29 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Generic
|
||||||
|
|
||||||
|
// ErrGenericForbidden represents a "UsernameAlreadyExists" kind of error.
|
||||||
|
type ErrGenericForbidden struct{}
|
||||||
|
|
||||||
|
// IsErrGenericForbidden checks if an error is a ErrGenericForbidden.
|
||||||
|
func IsErrGenericForbidden(err error) bool {
|
||||||
|
_, ok := err.(ErrGenericForbidden)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrGenericForbidden) Error() string {
|
||||||
|
return fmt.Sprintf("Forbidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCodeGenericForbidden holds the unique world-error code of this error
|
||||||
|
const ErrorCodeGenericForbidden = 0001
|
||||||
|
|
||||||
|
// HTTPError holds the http error description
|
||||||
|
func (err ErrGenericForbidden) HTTPError() web.HTTPError {
|
||||||
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrorCodeGenericForbidden, Message: "You're not allowed to do this."}
|
||||||
|
}
|
||||||
|
|
||||||
// =====================
|
// =====================
|
||||||
// User Operation Errors
|
// User Operation Errors
|
||||||
// =====================
|
// =====================
|
||||||
|
@ -423,6 +446,30 @@ func (err ErrListTitleCannotBeEmpty) HTTPError() web.HTTPError {
|
||||||
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrListShareDoesNotExist represents a "ErrListShareDoesNotExist" kind of error. Used if the list share does not exist.
|
||||||
|
type ErrListShareDoesNotExist struct {
|
||||||
|
ID int64
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrListShareDoesNotExist checks if an error is a ErrListShareDoesNotExist.
|
||||||
|
func IsErrListShareDoesNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrListShareDoesNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrListShareDoesNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("List share does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCodeListShareDoesNotExist holds the unique world-error code of this error
|
||||||
|
const ErrCodeListShareDoesNotExist = 3006
|
||||||
|
|
||||||
|
// HTTPError holds the http error description
|
||||||
|
func (err ErrListShareDoesNotExist) HTTPError() web.HTTPError {
|
||||||
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListShareDoesNotExist, Message: "The list share does not exist."}
|
||||||
|
}
|
||||||
|
|
||||||
// ================
|
// ================
|
||||||
// List task errors
|
// List task errors
|
||||||
// ================
|
// ================
|
||||||
|
|
24
pkg/models/fixtures/link_sharing.yml
Normal file
24
pkg/models/fixtures/link_sharing.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
- id: 1
|
||||||
|
hash: test
|
||||||
|
list_id: 1
|
||||||
|
right: 0
|
||||||
|
sharing_type: 1
|
||||||
|
shared_by_id: 1
|
||||||
|
created: 0
|
||||||
|
updated: 0
|
||||||
|
- id: 2
|
||||||
|
hash: test2
|
||||||
|
list_id: 2
|
||||||
|
right: 1
|
||||||
|
sharing_type: 1
|
||||||
|
shared_by_id: 1
|
||||||
|
created: 0
|
||||||
|
updated: 0
|
||||||
|
- id: 3
|
||||||
|
hash: test3
|
||||||
|
list_id: 3
|
||||||
|
right: 2
|
||||||
|
sharing_type: 1
|
||||||
|
shared_by_id: 1
|
||||||
|
created: 0
|
||||||
|
updated: 0
|
|
@ -198,4 +198,10 @@
|
||||||
hex_color: f0f0f0
|
hex_color: f0f0f0
|
||||||
created: 1543626724
|
created: 1543626724
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
|
- id: 32
|
||||||
|
text: 'task #32'
|
||||||
|
created_by_id: 1
|
||||||
|
list_id: 3
|
||||||
|
created: 1543626724
|
||||||
|
updated: 1543626724
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,9 @@ func (l *Label) Delete() (err error) {
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /labels [get]
|
// @Router /labels [get]
|
||||||
func (l *Label) ReadAll(search string, a web.Auth, page int) (ls interface{}, err error) {
|
func (l *Label) ReadAll(search string, a web.Auth, page int) (ls interface{}, err error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return nil, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
u := &User{ID: a.GetID()}
|
u := &User{ID: a.GetID()}
|
||||||
|
|
||||||
|
@ -192,7 +195,18 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
|
||||||
|
|
||||||
// Helper method to get all task ids a user has
|
// Helper method to get all task ids a user has
|
||||||
func getUserTaskIDs(u *User) (taskIDs []int64, err error) {
|
func getUserTaskIDs(u *User) (taskIDs []int64, err error) {
|
||||||
tasks, err := GetTasksByUser("", u, -1, SortTasksByUnsorted, time.Unix(0, 0), time.Unix(0, 0))
|
|
||||||
|
// Get all lists
|
||||||
|
lists, err := getRawListsForUser("", u, -1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks, err := getTasksForLists(lists, &taskOptions{
|
||||||
|
startDate: time.Unix(0, 0),
|
||||||
|
endDate: time.Unix(0, 0),
|
||||||
|
sortby: SortTasksByUnsorted,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,19 @@ func (l *Label) CanRead(a web.Auth) (bool, error) {
|
||||||
// CanCreate checks if the user can create a label
|
// CanCreate checks if the user can create a label
|
||||||
// Currently a dummy.
|
// Currently a dummy.
|
||||||
func (l *Label) CanCreate(a web.Auth) (bool, error) {
|
func (l *Label) CanCreate(a web.Auth) (bool, error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
|
func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
lorig, err := getLabelByIDSimple(l.ID)
|
lorig, err := getLabelByIDSimple(l.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -52,6 +61,9 @@ func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
// Helper method to check if a user can see a specific label
|
// Helper method to check if a user can see a specific label
|
||||||
func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
|
func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
|
// TODO: add an extra check for link share handling
|
||||||
|
|
||||||
// Get all tasks
|
// Get all tasks
|
||||||
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()})
|
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
207
pkg/models/link_sharing.go
Normal file
207
pkg/models/link_sharing.go
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Copyright 2019 Vikunja and contriubtors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of Vikunja.
|
||||||
|
//
|
||||||
|
// Vikunja is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Vikunja is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SharingType holds the sharing type
|
||||||
|
type SharingType int
|
||||||
|
|
||||||
|
// These consts represent all valid link sharing types
|
||||||
|
const (
|
||||||
|
SharingTypeUnknown SharingType = iota
|
||||||
|
SharingTypeWithoutPassword
|
||||||
|
SharingTypeWithPassword
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinkSharing represents a shared list
|
||||||
|
type LinkSharing struct {
|
||||||
|
// The ID of the shared thing
|
||||||
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"share"`
|
||||||
|
// The public id to get this shared list
|
||||||
|
Hash string `xorm:"varchar(40) not null unique" json:"hash" param:"hash"`
|
||||||
|
// The ID of the shared list
|
||||||
|
ListID int64 `xorm:"int(11) not null" json:"-" param:"list"`
|
||||||
|
// The right this list is shared with. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||||
|
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
|
List *List `xorm:"-" json:"list" param:"fullist"`
|
||||||
|
|
||||||
|
// The kind of this link. 0 = undefined, 1 = without password, 2 = with password (currently not implemented).
|
||||||
|
SharingType SharingType `xorm:"int(11) INDEX not null default 0" json:"sharing_type" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
|
// The user who shared this list
|
||||||
|
SharedBy *User `xorm:"-" json:"shared_by"`
|
||||||
|
SharedByID int64 `xorm:"int(11) INDEX not null" json:"-"`
|
||||||
|
|
||||||
|
// A unix timestamp when this list was shared. You cannot change this value.
|
||||||
|
Created int64 `xorm:"created not null" json:"created"`
|
||||||
|
// A unix timestamp when this share was last updated. You cannot change this value.
|
||||||
|
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName holds the table name
|
||||||
|
func (LinkSharing) TableName() string {
|
||||||
|
return "link_sharing"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID returns the ID of the links sharing object
|
||||||
|
func (share *LinkSharing) GetID() int64 {
|
||||||
|
return share.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinkShareFromClaims builds a link sharing object from jwt claims
|
||||||
|
func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error) {
|
||||||
|
share = &LinkSharing{}
|
||||||
|
share.ID = int64(claims["id"].(float64))
|
||||||
|
share.Hash = claims["hash"].(string)
|
||||||
|
share.ListID = int64(claims["listID"].(float64))
|
||||||
|
share.Right = Right(claims["right"].(float64))
|
||||||
|
share.SharedByID = int64(claims["sharedByID"].(float64))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new link share for a given list
|
||||||
|
// @Summary Share a list via link
|
||||||
|
// @Description Share a list via link. The user needs to have write-access to the list to be able do this.
|
||||||
|
// @tags sharing
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param list path int true "List ID"
|
||||||
|
// @Param label body models.LinkSharing true "The new link share object"
|
||||||
|
// @Success 200 {object} models.LinkSharing "The created link share object."
|
||||||
|
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid link share object provided."
|
||||||
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to add the list share."
|
||||||
|
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The list does not exist."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /lists/{list}/shares [put]
|
||||||
|
func (share *LinkSharing) Create(a web.Auth) (err error) {
|
||||||
|
share.SharedByID = a.GetID()
|
||||||
|
share.Hash = utils.MakeRandomString(40)
|
||||||
|
_, err = x.Insert(share)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOne returns one share
|
||||||
|
// @Summary Get one link shares for a list
|
||||||
|
// @Description Returns one link share by its ID.
|
||||||
|
// @tags sharing
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param list path int true "List ID"
|
||||||
|
// @Param share path int true "Share ID"
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Success 200 {object} models.LinkSharing "The share links"
|
||||||
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No access to the list"
|
||||||
|
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Share Link not found."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /lists/{list}/shares/{share} [get]
|
||||||
|
func (share *LinkSharing) ReadOne() (err error) {
|
||||||
|
exists, err := x.Where("id = ?", share.ID).Get(share)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return ErrListShareDoesNotExist{ID: share.ID, Hash: share.Hash}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAll returns all shares for a given list
|
||||||
|
// @Summary Get all link shares for a list
|
||||||
|
// @Description Returns all link shares which exist for a given list
|
||||||
|
// @tags sharing
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param list path int true "List ID"
|
||||||
|
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||||
|
// @Param s query string false "Search shares by hash."
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Success 200 {array} models.LinkSharing "The share links"
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /lists/{list}/shares [get]
|
||||||
|
func (share *LinkSharing) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
list := &List{ID: share.ListID}
|
||||||
|
can, err := list.CanRead(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !can {
|
||||||
|
return nil, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shares []*LinkSharing
|
||||||
|
err = x.
|
||||||
|
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
|
||||||
|
Limit(getLimitFromPageIndex(page)).
|
||||||
|
Find(&shares)
|
||||||
|
return shares, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a link share
|
||||||
|
// @Summary Remove a link share
|
||||||
|
// @Description Remove a link share. The user needs to have write-access to the list to be able do this.
|
||||||
|
// @tags sharing
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param list path int true "List ID"
|
||||||
|
// @Param share path int true "Share Link ID"
|
||||||
|
// @Success 200 {object} models.Message "The link was successfully removed."
|
||||||
|
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to remove the link."
|
||||||
|
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Share Link not found."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /lists/{list}/shares/{share} [delete]
|
||||||
|
func (share *LinkSharing) Delete() (err error) {
|
||||||
|
_, err = x.Where("id = ?", share.ID).Delete(share)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinkShareByHash returns a link share by hash
|
||||||
|
func GetLinkShareByHash(hash string) (share *LinkSharing, err error) {
|
||||||
|
share = &LinkSharing{}
|
||||||
|
has, err := x.Where("hash = ?", hash).Get(share)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return share, ErrListShareDoesNotExist{Hash: hash}
|
||||||
|
}
|
||||||
|
share.List = &List{ID: share.ListID}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListByShareHash returns a link share by its hash
|
||||||
|
func GetListByShareHash(hash string) (list *List, err error) {
|
||||||
|
share, err := GetLinkShareByHash(hash)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list = &List{ID: share.ListID}
|
||||||
|
err = list.GetSimpleByID()
|
||||||
|
return
|
||||||
|
}
|
61
pkg/models/link_sharing_rights.go
Normal file
61
pkg/models/link_sharing_rights.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
|
// Copyright 2019 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
|
// CanRead implements the read right check for a link share
|
||||||
|
func (share *LinkSharing) CanRead(a web.Auth) (bool, error) {
|
||||||
|
// Don't allow creating link shares if the user itself authenticated with a link share
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := GetListByShareHash(share.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return l.CanRead(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDelete implements the delete right check for a link share
|
||||||
|
func (share *LinkSharing) CanDelete(a web.Auth) (bool, error) {
|
||||||
|
return share.canDoLinkShare(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanUpdate implements the update right check for a link share
|
||||||
|
func (share *LinkSharing) CanUpdate(a web.Auth) (bool, error) {
|
||||||
|
return share.canDoLinkShare(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanCreate implements the create right check for a link share
|
||||||
|
func (share *LinkSharing) CanCreate(a web.Auth) (bool, error) {
|
||||||
|
return share.canDoLinkShare(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (share *LinkSharing) canDoLinkShare(a web.Auth) (bool, error) {
|
||||||
|
// Don't allow creating link shares if the user itself authenticated with a link share
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := GetListByShareHash(share.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return l.CanWrite(a)
|
||||||
|
}
|
|
@ -85,14 +85,26 @@ func GetListsByNamespaceID(nID int64, doer *User) (lists []*List, err error) {
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists [get]
|
// @Router /lists [get]
|
||||||
func (l *List) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
func (l *List) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
// Check if we're dealing with a share auth
|
||||||
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
|
if ok {
|
||||||
|
shareAuth.List = &List{ID: shareAuth.ListID}
|
||||||
|
err := shareAuth.List.GetSimpleByID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lists := []*List{shareAuth.List}
|
||||||
|
err = AddListDetails(lists)
|
||||||
|
return lists, err
|
||||||
|
}
|
||||||
|
|
||||||
lists, err := getRawListsForUser(search, &User{ID: a.GetID()}, page)
|
lists, err := getRawListsForUser(search, &User{ID: a.GetID()}, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add more list details
|
// Add more list details
|
||||||
AddListDetails(lists)
|
err = AddListDetails(lists)
|
||||||
|
|
||||||
return lists, err
|
return lists, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,13 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're dealing with a share auth
|
||||||
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
|
if ok {
|
||||||
|
return originalList.ID == shareAuth.ListID &&
|
||||||
|
(shareAuth.Right == RightWrite || shareAuth.Right == RightAdmin), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user is either owner or can write to the list
|
// Check if the user is either owner or can write to the list
|
||||||
if originalList.isOwner(&User{ID: a.GetID()}) {
|
if originalList.isOwner(&User{ID: a.GetID()}) {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -45,6 +52,14 @@ func (l *List) CanRead(a web.Auth) (bool, error) {
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're dealing with a share auth
|
||||||
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
|
if ok {
|
||||||
|
return l.ID == shareAuth.ListID &&
|
||||||
|
(shareAuth.Right == RightRead || shareAuth.Right == RightWrite || shareAuth.Right == RightAdmin), nil
|
||||||
|
}
|
||||||
|
|
||||||
if l.isOwner(&User{ID: a.GetID()}) {
|
if l.isOwner(&User{ID: a.GetID()}) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -61,7 +76,7 @@ func (l *List) CanDelete(a web.Auth) (bool, error) {
|
||||||
return l.IsAdmin(a)
|
return l.IsAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreate checks if the user can update a list
|
// CanCreate checks if the user can create a list
|
||||||
func (l *List) CanCreate(a web.Auth) (bool, error) {
|
func (l *List) CanCreate(a web.Auth) (bool, error) {
|
||||||
// A user can create a list if he has write access to the namespace
|
// A user can create a list if he has write access to the namespace
|
||||||
n := &Namespace{ID: l.NamespaceID}
|
n := &Namespace{ID: l.NamespaceID}
|
||||||
|
@ -76,6 +91,12 @@ func (l *List) IsAdmin(a web.Auth) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're dealing with a share auth
|
||||||
|
shareAuth, ok := a.(*LinkSharing)
|
||||||
|
if ok {
|
||||||
|
return originalList.ID == shareAuth.ListID && shareAuth.Right == RightAdmin, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check all the things
|
// Check all the things
|
||||||
// Check if the user is either owner or can write to the list
|
// Check if the user is either owner or can write to the list
|
||||||
// Owners are always admins
|
// Owners are always admins
|
||||||
|
|
|
@ -22,20 +22,25 @@ import (
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new user <-> list relation
|
// CanCreate checks if the user can create a new user <-> list relation
|
||||||
func (lu *ListUser) CanCreate(a web.Auth) (bool, error) {
|
func (lu *ListUser) CanCreate(a web.Auth) (bool, error) {
|
||||||
// Get the list and check if the user has write access on it
|
return lu.canDoListUser(a)
|
||||||
l := List{ID: lu.ListID}
|
|
||||||
return l.CanWrite(a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a user <-> list relation
|
// CanDelete checks if the user can delete a user <-> list relation
|
||||||
func (lu *ListUser) CanDelete(a web.Auth) (bool, error) {
|
func (lu *ListUser) CanDelete(a web.Auth) (bool, error) {
|
||||||
// Get the list and check if the user has write access on it
|
return lu.canDoListUser(a)
|
||||||
l := List{ID: lu.ListID}
|
|
||||||
return l.CanWrite(a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a user <-> list relation
|
// CanUpdate checks if the user can update a user <-> list relation
|
||||||
func (lu *ListUser) CanUpdate(a web.Auth) (bool, error) {
|
func (lu *ListUser) CanUpdate(a web.Auth) (bool, error) {
|
||||||
|
return lu.canDoListUser(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lu *ListUser) canDoListUser(a web.Auth) (bool, error) {
|
||||||
|
// Link shares aren't allowed to do anything
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get the list and check if the user has write access on it
|
// Get the list and check if the user has write access on it
|
||||||
l := List{ID: lu.ListID}
|
l := List{ID: lu.ListID}
|
||||||
return l.CanWrite(a)
|
return l.CanWrite(a)
|
||||||
|
|
|
@ -48,6 +48,7 @@ func GetTables() []interface{} {
|
||||||
&Label{},
|
&Label{},
|
||||||
&LabelTask{},
|
&LabelTask{},
|
||||||
&TaskReminder{},
|
&TaskReminder{},
|
||||||
|
&LinkSharing{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,10 @@ type NamespaceWithLists struct {
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces [get]
|
// @Router /namespaces [get]
|
||||||
func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return nil, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
doer, err := getUserWithError(a)
|
doer, err := getUserWithError(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -48,12 +48,21 @@ func (n *Namespace) CanDelete(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new namespace
|
// CanCreate checks if the user can create a new namespace
|
||||||
func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
|
func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// This is currently a dummy function, later on we could imagine global limits etc.
|
// This is currently a dummy function, later on we could imagine global limits etc.
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, error) {
|
func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, error) {
|
||||||
|
|
||||||
|
// If the auth is a link share, don't do anything
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get the namespace and check the right
|
// Get the namespace and check the right
|
||||||
err := n.GetSimpleByID()
|
err := n.GetSimpleByID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -221,13 +221,27 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||||
// @Param s query string false "Search assignees by their username."
|
// @Param s query string false "Search assignees by their username."
|
||||||
|
// @Param taskID path int true "Task ID"
|
||||||
// @Security JWTKeyAuth
|
// @Security JWTKeyAuth
|
||||||
// @Success 200 {array} models.User "The assignees"
|
// @Success 200 {array} models.User "The assignees"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /labels [get]
|
// @Router /tasks/{taskID}/assignees [get]
|
||||||
func (la *TaskAssginee) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
func (la *TaskAssginee) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
task, err := GetListSimplByTaskID(la.TaskID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
can, err := task.CanRead(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !can {
|
||||||
|
return nil, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
var taskAssignees []*User
|
var taskAssignees []*User
|
||||||
err := x.Table("task_assignees").
|
err = x.Table("task_assignees").
|
||||||
Select("users.*").
|
Select("users.*").
|
||||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
||||||
|
|
|
@ -41,5 +41,5 @@ func canDoTaskAssingee(taskID int64, a web.Auth) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return list.CanCreate(a)
|
return list.CanUpdate(a)
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,6 +317,15 @@ func sortTasksForTesting(by SortBy) (tasks []*Task) {
|
||||||
Created: 1543626724,
|
Created: 1543626724,
|
||||||
Updated: 1543626724,
|
Updated: 1543626724,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: 32,
|
||||||
|
Text: "task #32",
|
||||||
|
CreatedByID: 1,
|
||||||
|
CreatedBy: user1,
|
||||||
|
ListID: 3,
|
||||||
|
Created: 1543626724,
|
||||||
|
Updated: 1543626724,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
switch by {
|
switch by {
|
||||||
|
|
|
@ -147,17 +147,40 @@ func (t *Task) ReadAll(search string, a web.Auth, page int) (interface{}, error)
|
||||||
sortby = SortTasksByUnsorted
|
sortby = SortTasksByUnsorted
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetTasksByUser(search, &User{ID: a.GetID()}, page, sortby, time.Unix(t.StartDateSortUnix, 0), time.Unix(t.EndDateSortUnix, 0))
|
taskopts := &taskOptions{
|
||||||
}
|
search: search,
|
||||||
|
sortby: sortby,
|
||||||
|
startDate: time.Unix(t.StartDateSortUnix, 0),
|
||||||
|
endDate: time.Unix(t.EndDateSortUnix, 0),
|
||||||
|
}
|
||||||
|
|
||||||
//GetTasksByUser returns all tasks for a user
|
shareAuth, is := a.(*LinkSharing)
|
||||||
func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate time.Time, endDate time.Time) ([]*Task, error) {
|
if is {
|
||||||
// Get all lists
|
shareAuth.List = &List{ID: shareAuth.ListID}
|
||||||
lists, err := getRawListsForUser("", u, page)
|
err := shareAuth.List.GetSimpleByID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getTasksForLists([]*List{shareAuth.List}, taskopts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all lists for the user
|
||||||
|
lists, err := getRawListsForUser("", &User{ID: a.GetID()}, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return getTasksForLists(lists, taskopts)
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskOptions struct {
|
||||||
|
search string
|
||||||
|
sortby SortBy
|
||||||
|
startDate time.Time
|
||||||
|
endDate time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, err error) {
|
||||||
// Get all list IDs and get the tasks
|
// Get all list IDs and get the tasks
|
||||||
var listIDs []int64
|
var listIDs []int64
|
||||||
for _, l := range lists {
|
for _, l := range lists {
|
||||||
|
@ -165,7 +188,7 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderby string
|
var orderby string
|
||||||
switch sortby {
|
switch opts.sortby {
|
||||||
case SortTasksByPriorityDesc:
|
case SortTasksByPriorityDesc:
|
||||||
orderby = "priority desc"
|
orderby = "priority desc"
|
||||||
case SortTasksByPriorityAsc:
|
case SortTasksByPriorityAsc:
|
||||||
|
@ -179,20 +202,20 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
|
||||||
taskMap := make(map[int64]*Task)
|
taskMap := make(map[int64]*Task)
|
||||||
|
|
||||||
// Then return all tasks for that lists
|
// Then return all tasks for that lists
|
||||||
if startDate.Unix() != 0 || endDate.Unix() != 0 {
|
if opts.startDate.Unix() != 0 || opts.endDate.Unix() != 0 {
|
||||||
|
|
||||||
startDateUnix := time.Now().Unix()
|
startDateUnix := time.Now().Unix()
|
||||||
if startDate.Unix() != 0 {
|
if opts.startDate.Unix() != 0 {
|
||||||
startDateUnix = startDate.Unix()
|
startDateUnix = opts.startDate.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
endDateUnix := time.Now().Unix()
|
endDateUnix := time.Now().Unix()
|
||||||
if endDate.Unix() != 0 {
|
if opts.endDate.Unix() != 0 {
|
||||||
endDateUnix = endDate.Unix()
|
endDateUnix = opts.endDate.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := x.In("list_id", listIDs).
|
if err := x.In("list_id", listIDs).
|
||||||
Where("text LIKE ?", "%"+search+"%").
|
Where("text LIKE ?", "%"+opts.search+"%").
|
||||||
And("((due_date_unix BETWEEN ? AND ?) OR "+
|
And("((due_date_unix BETWEEN ? AND ?) OR "+
|
||||||
"(start_date_unix BETWEEN ? and ?) OR "+
|
"(start_date_unix BETWEEN ? and ?) OR "+
|
||||||
"(end_date_unix BETWEEN ? and ?))", startDateUnix, endDateUnix, startDateUnix, endDateUnix, startDateUnix, endDateUnix).
|
"(end_date_unix BETWEEN ? and ?))", startDateUnix, endDateUnix, startDateUnix, endDateUnix, startDateUnix, endDateUnix).
|
||||||
|
@ -203,7 +226,7 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := x.In("list_id", listIDs).
|
if err := x.In("list_id", listIDs).
|
||||||
Where("text LIKE ?", "%"+search+"%").
|
Where("text LIKE ?", "%"+opts.search+"%").
|
||||||
And("(parent_task_id = 0 OR parent_task_id IS NULL)").
|
And("(parent_task_id = 0 OR parent_task_id IS NULL)").
|
||||||
OrderBy(orderby).
|
OrderBy(orderby).
|
||||||
Find(&taskMap); err != nil {
|
Find(&taskMap); err != nil {
|
||||||
|
@ -211,13 +234,13 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks, err := addMoreInfoToTasks(taskMap)
|
tasks, err = addMoreInfoToTasks(taskMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Because the list is sorted by id which we don't want (since we're dealing with maps)
|
// Because the list is sorted by id which we don't want (since we're dealing with maps)
|
||||||
// we have to manually sort the tasks again here.
|
// we have to manually sort the tasks again here.
|
||||||
sortTasks(tasks, sortby)
|
sortTasks(tasks, opts.sortby)
|
||||||
|
|
||||||
return tasks, err
|
return tasks, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,8 @@ func (t *Task) CanRead(a web.Auth) (canRead bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A user can read a task if it has access to the list
|
// A user can read a task if it has access to the list
|
||||||
list := &List{ID: t.ListID}
|
l := &List{ID: t.ListID}
|
||||||
return list.CanRead(a)
|
return l.CanRead(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check if a user can do stuff on a list task
|
// Helper function to check if a user can do stuff on a list task
|
||||||
|
|
|
@ -36,6 +36,11 @@ func (tl *TeamList) CanUpdate(a web.Auth) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tl *TeamList) canDoTeamList(a web.Auth) (bool, error) {
|
func (tl *TeamList) canDoTeamList(a web.Auth) (bool, error) {
|
||||||
|
// Link shares aren't allowed to do anything
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
l := List{ID: tl.ListID}
|
l := List{ID: tl.ListID}
|
||||||
return l.IsAdmin(a)
|
return l.IsAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,11 @@ func (tm *TeamMember) CanDelete(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
// IsAdmin checks if the user is team admin
|
// IsAdmin checks if the user is team admin
|
||||||
func (tm *TeamMember) IsAdmin(a web.Auth) (bool, error) {
|
func (tm *TeamMember) IsAdmin(a web.Auth) (bool, error) {
|
||||||
|
// Don't allow anything if we're dealing with a list share here
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// A user can add a member to a team if he is admin of that team
|
// A user can add a member to a team if he is admin of that team
|
||||||
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", a.GetID(), tm.TeamID, true).
|
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", a.GetID(), tm.TeamID, true).
|
||||||
Get(&TeamMember{})
|
Get(&TeamMember{})
|
||||||
|
|
|
@ -142,6 +142,10 @@ func (t *Team) ReadOne() (err error) {
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /teams [get]
|
// @Router /teams [get]
|
||||||
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return nil, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
all := []*Team{}
|
all := []*Team{}
|
||||||
err := x.Select("teams.*").
|
err := x.Select("teams.*").
|
||||||
Table("teams").
|
Table("teams").
|
||||||
|
|
|
@ -22,6 +22,10 @@ import (
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new team
|
// CanCreate checks if the user can create a new team
|
||||||
func (t *Team) CanCreate(a web.Auth) (bool, error) {
|
func (t *Team) CanCreate(a web.Auth) (bool, error) {
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// This is currently a dummy function, later on we could imagine global limits etc.
|
// This is currently a dummy function, later on we could imagine global limits etc.
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -38,6 +42,11 @@ func (t *Team) CanDelete(a web.Auth) (bool, error) {
|
||||||
|
|
||||||
// IsAdmin returns true when the user is admin of a team
|
// IsAdmin returns true when the user is admin of a team
|
||||||
func (t *Team) IsAdmin(a web.Auth) (bool, error) {
|
func (t *Team) IsAdmin(a web.Auth) (bool, error) {
|
||||||
|
// Don't do anything if we're deadling with a link share auth here
|
||||||
|
if _, is := a.(*LinkSharing); is {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the team exists to be able to return a proper error message if not
|
// Check if the team exists to be able to return a proper error message if not
|
||||||
_, err := GetTeamByID(t.ID)
|
_, err := GetTeamByID(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -189,6 +189,11 @@ func CheckUserCredentials(u *UserLogin) (*User, error) {
|
||||||
func GetCurrentUser(c echo.Context) (user *User, err error) {
|
func GetCurrentUser(c echo.Context) (user *User, err error) {
|
||||||
jwtinf := c.Get("user").(*jwt.Token)
|
jwtinf := c.Get("user").(*jwt.Token)
|
||||||
claims := jwtinf.Claims.(jwt.MapClaims)
|
claims := jwtinf.Claims.(jwt.MapClaims)
|
||||||
|
return GetUserFromClaims(claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserFromClaims Returns a new user from jwt claims
|
||||||
|
func GetUserFromClaims(claims jwt.MapClaims) (user *User, err error) {
|
||||||
userID, ok := claims["id"].(float64)
|
userID, ok := claims["id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return user, ErrCouldNotGetUserID{}
|
return user, ErrCouldNotGetUserID{}
|
||||||
|
|
67
pkg/routes/api/v1/auth.go
Normal file
67
pkg/routes/api/v1/auth.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
|
// Copyright 2019 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/config"
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are all valid auth types
|
||||||
|
const (
|
||||||
|
AuthTypeUnknown int = iota
|
||||||
|
AuthTypeUser
|
||||||
|
AuthTypeLinkShare
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewUserJWTAuthtoken generates and signes a new jwt token for a user. This is a global function to be able to call it from integration tests.
|
||||||
|
func NewUserJWTAuthtoken(user *models.User) (token string, err error) {
|
||||||
|
t := jwt.New(jwt.SigningMethodHS256)
|
||||||
|
|
||||||
|
// Set claims
|
||||||
|
claims := t.Claims.(jwt.MapClaims)
|
||||||
|
claims["type"] = AuthTypeUser
|
||||||
|
claims["id"] = user.ID
|
||||||
|
claims["username"] = user.Username
|
||||||
|
claims["email"] = user.Email
|
||||||
|
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||||
|
|
||||||
|
claims["avatar"] = user.AvatarURL
|
||||||
|
|
||||||
|
// Generate encoded token and send it as response.
|
||||||
|
return t.SignedString([]byte(config.ServiceJWTSecret.GetString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLinkShareJWTAuthtoken creates a new jwt token from a link share
|
||||||
|
func NewLinkShareJWTAuthtoken(share *models.LinkSharing) (token string, err error) {
|
||||||
|
t := jwt.New(jwt.SigningMethodHS256)
|
||||||
|
|
||||||
|
// Set claims
|
||||||
|
claims := t.Claims.(jwt.MapClaims)
|
||||||
|
claims["type"] = AuthTypeLinkShare
|
||||||
|
claims["id"] = share.ID
|
||||||
|
claims["hash"] = share.Hash
|
||||||
|
claims["listID"] = share.ListID
|
||||||
|
claims["right"] = share.Right
|
||||||
|
claims["sharedByID"] = share.SharedByID
|
||||||
|
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||||
|
|
||||||
|
// Generate encoded token and send it as response.
|
||||||
|
return t.SignedString([]byte(config.ServiceJWTSecret.GetString()))
|
||||||
|
}
|
|
@ -24,22 +24,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type vikunjaInfos struct {
|
type vikunjaInfos struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
FrontendURL string `json:"frontend_url"`
|
FrontendURL string `json:"frontend_url"`
|
||||||
Motd string `json:"motd"`
|
Motd string `json:"motd"`
|
||||||
|
LinkSharingEnabled bool `json:"link_sharing_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info is the handler to get infos about this vikunja instance
|
// Info is the handler to get infos about this vikunja instance
|
||||||
// @Summary Info
|
// @Summary Info
|
||||||
// @Description Returns the version, frontendurl and motd of Vikunja
|
// @Description Returns the version, frontendurl, motd and various settings of Vikunja
|
||||||
// @tags service
|
// @tags service
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} v1.vikunjaInfos
|
// @Success 200 {object} v1.vikunjaInfos
|
||||||
// @Router /info [get]
|
// @Router /info [get]
|
||||||
func Info(c echo.Context) error {
|
func Info(c echo.Context) error {
|
||||||
return c.JSON(http.StatusOK, vikunjaInfos{
|
return c.JSON(http.StatusOK, vikunjaInfos{
|
||||||
Version: version.Version,
|
Version: version.Version,
|
||||||
FrontendURL: config.ServiceFrontendurl.GetString(),
|
FrontendURL: config.ServiceFrontendurl.GetString(),
|
||||||
Motd: config.ServiceMotd.GetString(),
|
Motd: config.ServiceMotd.GetString(),
|
||||||
|
LinkSharingEnabled: config.ServiceEnableLinkSharing.GetBool(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
50
pkg/routes/api/v1/link_sharing_auth.go
Normal file
50
pkg/routes/api/v1/link_sharing_auth.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
|
// Copyright 2019 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/web/handler"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthenticateLinkShare gives a jwt auth token for valid share hashes
|
||||||
|
// @Summary Get an auth token for a share
|
||||||
|
// @Description Get a jwt auth token for a shared list from a share hash.
|
||||||
|
// @tags sharing
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param share path string true "The share hash"
|
||||||
|
// @Success 200 {object} v1.Token "The valid jwt auth token."
|
||||||
|
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid link share object provided."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /shares/{share}/auth [post]
|
||||||
|
func AuthenticateLinkShare(c echo.Context) error {
|
||||||
|
hash := c.Param("share")
|
||||||
|
share, err := models.GetLinkShareByHash(hash)
|
||||||
|
if err != nil {
|
||||||
|
return handler.HandleHTTPError(err, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := NewLinkShareJWTAuthtoken(share)
|
||||||
|
if err != nil {
|
||||||
|
return handler.HandleHTTPError(err, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, Token{Token: t})
|
||||||
|
}
|
|
@ -17,13 +17,10 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/web/handler"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Token represents an authentification token
|
// Token represents an authentification token
|
||||||
|
@ -55,27 +52,10 @@ func Login(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create token
|
// Create token
|
||||||
t, err := CreateNewJWTTokenForUser(user)
|
t, err := NewUserJWTAuthtoken(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, Token{Token: t})
|
return c.JSON(http.StatusOK, Token{Token: t})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewJWTTokenForUser generates and signes a new jwt token for a user. This is a global function to be able to call it from integration tests.
|
|
||||||
func CreateNewJWTTokenForUser(user *models.User) (token string, err error) {
|
|
||||||
t := jwt.New(jwt.SigningMethodHS256)
|
|
||||||
|
|
||||||
// Set claims
|
|
||||||
claims := t.Claims.(jwt.MapClaims)
|
|
||||||
claims["username"] = user.Username
|
|
||||||
claims["email"] = user.Email
|
|
||||||
claims["id"] = user.ID
|
|
||||||
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
|
||||||
|
|
||||||
claims["avatar"] = user.AvatarURL
|
|
||||||
|
|
||||||
// Generate encoded token and send it as response.
|
|
||||||
return t.SignedString([]byte(config.ServiceJWTSecret.GetString()))
|
|
||||||
}
|
|
||||||
|
|
|
@ -48,9 +48,11 @@ import (
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/web/handler"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
elog "github.com/labstack/gommon/log"
|
elog "github.com/labstack/gommon/log"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,11 +69,11 @@ func (cv *CustomValidator) Validate(i interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
httperr := models.ValidationHTTPError{
|
httperr := models.ValidationHTTPError{
|
||||||
web.HTTPError{
|
HTTPError: web.HTTPError{
|
||||||
Code: models.ErrCodeInvalidData,
|
Code: models.ErrCodeInvalidData,
|
||||||
Message: "Invalid Data",
|
Message: "Invalid Data",
|
||||||
},
|
},
|
||||||
errs,
|
InvalidFields: errs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return httperr
|
return httperr
|
||||||
|
@ -108,7 +110,16 @@ func NewEcho() *echo.Echo {
|
||||||
// Handler config
|
// Handler config
|
||||||
handler.SetAuthProvider(&web.Auths{
|
handler.SetAuthProvider(&web.Auths{
|
||||||
AuthObject: func(c echo.Context) (web.Auth, error) {
|
AuthObject: func(c echo.Context) (web.Auth, error) {
|
||||||
return models.GetCurrentUser(c)
|
jwtinf := c.Get("user").(*jwt.Token)
|
||||||
|
claims := jwtinf.Claims.(jwt.MapClaims)
|
||||||
|
typ := int(claims["type"].(float64))
|
||||||
|
if typ == apiv1.AuthTypeLinkShare && config.ServiceEnableLinkSharing.GetBool() {
|
||||||
|
return models.GetLinkShareFromClaims(claims)
|
||||||
|
}
|
||||||
|
if typ == apiv1.AuthTypeUser {
|
||||||
|
return models.GetUserFromClaims(claims)
|
||||||
|
}
|
||||||
|
return nil, echo.NewHTTPError(http.StatusBadRequest, models.Message{Message: "Invalid JWT token."})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
handler.SetLoggingProvider(log.GetLogger())
|
handler.SetLoggingProvider(log.GetLogger())
|
||||||
|
@ -165,6 +176,11 @@ func registerAPIRoutes(a *echo.Group) {
|
||||||
// Info endpoint
|
// Info endpoint
|
||||||
a.GET("/info", apiv1.Info)
|
a.GET("/info", apiv1.Info)
|
||||||
|
|
||||||
|
// Link share auth
|
||||||
|
if config.ServiceEnableLinkSharing.GetBool() {
|
||||||
|
a.POST("/shares/:share/auth", apiv1.AuthenticateLinkShare)
|
||||||
|
}
|
||||||
|
|
||||||
// ===== Routes with Authetication =====
|
// ===== Routes with Authetication =====
|
||||||
// Authetification
|
// Authetification
|
||||||
a.Use(middleware.JWT([]byte(config.ServiceJWTSecret.GetString())))
|
a.Use(middleware.JWT([]byte(config.ServiceJWTSecret.GetString())))
|
||||||
|
@ -194,6 +210,18 @@ func registerAPIRoutes(a *echo.Group) {
|
||||||
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
|
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
|
||||||
a.GET("/lists/:list/listusers", apiv1.ListUsersForList)
|
a.GET("/lists/:list/listusers", apiv1.ListUsersForList)
|
||||||
|
|
||||||
|
if config.ServiceEnableLinkSharing.GetBool() {
|
||||||
|
listSharingHandler := &handler.WebHandler{
|
||||||
|
EmptyStruct: func() handler.CObject {
|
||||||
|
return &models.LinkSharing{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.PUT("/lists/:list/shares", listSharingHandler.CreateWeb)
|
||||||
|
a.GET("/lists/:list/shares", listSharingHandler.ReadAllWeb)
|
||||||
|
a.GET("/lists/:list/shares/:share", listSharingHandler.ReadOneWeb)
|
||||||
|
a.DELETE("/lists/:list/shares/:share", listSharingHandler.DeleteWeb)
|
||||||
|
}
|
||||||
|
|
||||||
taskHandler := &handler.WebHandler{
|
taskHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() handler.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.Task{}
|
return &models.Task{}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
// This file was generated by swaggo/swag at
|
// This file was generated by swaggo/swag at
|
||||||
// 2019-07-21 23:56:59.485456403 +0200 CEST m=+0.091837160
|
// 2019-08-31 22:48:49.201391811 +0200 CEST m=+0.228973511
|
||||||
|
|
||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ var doc = `{
|
||||||
"paths": {
|
"paths": {
|
||||||
"/info": {
|
"/info": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns the version, frontendurl and motd of Vikunja",
|
"description": "Returns the version, frontendurl, motd and various settings of Vikunja",
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -58,7 +58,7 @@ var doc = `{
|
||||||
"JWTKeyAuth": []
|
"JWTKeyAuth": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Returns an array with all assignees for this task.",
|
"description": "Returns all labels which are either created by the user or associated with a task the user has at least read-access to.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -66,9 +66,9 @@ var doc = `{
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"assignees"
|
"labels"
|
||||||
],
|
],
|
||||||
"summary": "Get all assignees for a task",
|
"summary": "Get all labels a user has access to",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -78,18 +78,18 @@ var doc = `{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Search assignees by their username.",
|
"description": "Search labels by label text.",
|
||||||
"name": "s",
|
"name": "s",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "The assignees",
|
"description": "The labels",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.User"
|
"$ref": "#/definitions/models.Label"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1266,6 +1266,269 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/lists/{list}/shares": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns all link shares which exist for a given list",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get all link shares for a list",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.",
|
||||||
|
"name": "p",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search shares by hash.",
|
||||||
|
"name": "s",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The share links",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Share a list via link. The user needs to have write-access to the list to be able do this.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Share a list via link",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "The new link share object",
|
||||||
|
"name": "label",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The created link share object.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid link share object provided.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Not allowed to add the list share.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "The list does not exist.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/lists/{list}/shares/{share}": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns one link share by its ID.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get one link shares for a list",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Share ID",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The share links",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "No access to the list",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Share Link not found.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Remove a link share. The user needs to have write-access to the list to be able do this.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Remove a link share",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Share Link ID",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The link was successfully removed.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Not allowed to remove the link.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Share Link not found.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/login": {
|
"/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Logs a user in. Returns a JWT-Token to authenticate further requests.",
|
"description": "Logs a user in. Returns a JWT-Token to authenticate further requests.",
|
||||||
|
@ -2337,6 +2600,53 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/shares/{share}/auth": {
|
||||||
|
"post": {
|
||||||
|
"description": "Get a jwt auth token for a shared list from a share hash.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get an auth token for a share",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The share hash",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The valid jwt auth token.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/v1.Token"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid link share object provided.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/tasks/all": {
|
"/tasks/all": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -2593,6 +2903,63 @@ var doc = `{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/tasks/{taskID}/assignees": {
|
"/tasks/{taskID}/assignees": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns an array with all assignees for this task.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assignees"
|
||||||
|
],
|
||||||
|
"summary": "Get all assignees for a task",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.",
|
||||||
|
"name": "p",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search assignees by their username.",
|
||||||
|
"name": "s",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Task ID",
|
||||||
|
"name": "taskID",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The assignees",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"put": {
|
"put": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
@ -3896,6 +4263,48 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.LinkSharing": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"description": "A unix timestamp when this list was shared. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"description": "The public id to get this shared list",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "The ID of the shared thing",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.List"
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"description": "The right this list is shared with. 0 = Read only, 1 = Read \u0026 Write, 2 = Admin. See the docs for more details.",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 0,
|
||||||
|
"maximum": 2
|
||||||
|
},
|
||||||
|
"shared_by": {
|
||||||
|
"description": "The user who shared this list",
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
},
|
||||||
|
"sharing_type": {
|
||||||
|
"description": "The kind of this link. 0 = undefined, 1 = without password, 2 = with password (currently not implemented).",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 0,
|
||||||
|
"maximum": 2
|
||||||
|
},
|
||||||
|
"updated": {
|
||||||
|
"description": "A unix timestamp when this share was last updated. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.List": {
|
"models.List": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -3935,118 +4344,6 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models.Task": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"assignees": {
|
|
||||||
"description": "An array of users who are assigned to this task",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.User"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created": {
|
|
||||||
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"createdBy": {
|
|
||||||
"description": "The user who initially created the task.",
|
|
||||||
"type": "object",
|
|
||||||
"$ref": "#/definitions/models.User"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"description": "The task description.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"done": {
|
|
||||||
"description": "Whether a task is done or not.",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"doneAt": {
|
|
||||||
"description": "The unix timestamp when a task was marked as done.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"dueDate": {
|
|
||||||
"description": "A unix timestamp when the task is due.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"endDate": {
|
|
||||||
"description": "When this task ends.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"hexColor": {
|
|
||||||
"description": "The task color in hex",
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 6
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"description": "The unique, numeric id of this task.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"description": "An array of labels which are associated with this task.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.Label"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"listID": {
|
|
||||||
"description": "The list this task belongs to.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"parentTaskID": {
|
|
||||||
"description": "If the task is a subtask, this is the id of its parent.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"priority": {
|
|
||||||
"description": "The task priority. Can be anything you want, it is possible to sort by this later.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"reminderDates": {
|
|
||||||
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repeatAfter": {
|
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"startDate": {
|
|
||||||
"description": "When this task starts.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"subtasks": {
|
|
||||||
"description": "An array of subtasks.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.Task"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"text": {
|
|
||||||
"description": "The task text. This is what you'll see in the list.",
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 250,
|
|
||||||
"minLength": 3
|
|
||||||
},
|
|
||||||
"updated": {
|
|
||||||
"description": "A unix timestamp when this task was last updated. You cannot change this value.",
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models.TaskAssginee": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models.ListUser": {
|
"models.ListUser": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -4202,6 +4499,118 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.Task": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"description": "An array of users who are assigned to this task",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"createdBy": {
|
||||||
|
"description": "The user who initially created the task.",
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"description": "The task description.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"done": {
|
||||||
|
"description": "Whether a task is done or not.",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"doneAt": {
|
||||||
|
"description": "The unix timestamp when a task was marked as done.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"dueDate": {
|
||||||
|
"description": "A unix timestamp when the task is due.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"endDate": {
|
||||||
|
"description": "When this task ends.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"hexColor": {
|
||||||
|
"description": "The task color in hex",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 6
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "The unique, numeric id of this task.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"description": "An array of labels which are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Label"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"listID": {
|
||||||
|
"description": "The list this task belongs to.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"parentTaskID": {
|
||||||
|
"description": "If the task is a subtask, this is the id of its parent.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"description": "The task priority. Can be anything you want, it is possible to sort by this later.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"reminderDates": {
|
||||||
|
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeatAfter": {
|
||||||
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"startDate": {
|
||||||
|
"description": "When this task starts.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"subtasks": {
|
||||||
|
"description": "An array of subtasks.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Task"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"description": "The task text. This is what you'll see in the list.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 250,
|
||||||
|
"minLength": 3
|
||||||
|
},
|
||||||
|
"updated": {
|
||||||
|
"description": "A unix timestamp when this task was last updated. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.TaskAssginee": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Team": {
|
"models.Team": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -4499,6 +4908,9 @@ var doc = `{
|
||||||
"frontend_url": {
|
"frontend_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"link_sharing_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"motd": {
|
"motd": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"/info": {
|
"/info": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns the version, frontendurl and motd of Vikunja",
|
"description": "Returns the version, frontendurl, motd and various settings of Vikunja",
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
"JWTKeyAuth": []
|
"JWTKeyAuth": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Returns an array with all assignees for this task.",
|
"description": "Returns all labels which are either created by the user or associated with a task the user has at least read-access to.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -53,9 +53,9 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"assignees"
|
"labels"
|
||||||
],
|
],
|
||||||
"summary": "Get all assignees for a task",
|
"summary": "Get all labels a user has access to",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -65,18 +65,18 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Search assignees by their username.",
|
"description": "Search labels by label text.",
|
||||||
"name": "s",
|
"name": "s",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "The assignees",
|
"description": "The labels",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.User"
|
"$ref": "#/definitions/models.Label"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -475,7 +475,7 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -484,7 +484,7 @@
|
||||||
"description": "The created task object.",
|
"description": "The created task object.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
@ -1253,6 +1253,269 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/lists/{list}/shares": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns all link shares which exist for a given list",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get all link shares for a list",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.",
|
||||||
|
"name": "p",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search shares by hash.",
|
||||||
|
"name": "s",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The share links",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Share a list via link. The user needs to have write-access to the list to be able do this.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Share a list via link",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "The new link share object",
|
||||||
|
"name": "label",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The created link share object.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid link share object provided.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Not allowed to add the list share.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "The list does not exist.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/lists/{list}/shares/{share}": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns one link share by its ID.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get one link shares for a list",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Share ID",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The share links",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.LinkSharing"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "No access to the list",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Share Link not found.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Remove a link share. The user needs to have write-access to the list to be able do this.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Remove a link share",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "List ID",
|
||||||
|
"name": "list",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Share Link ID",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The link was successfully removed.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Not allowed to remove the link.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Share Link not found.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/login": {
|
"/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Logs a user in. Returns a JWT-Token to authenticate further requests.",
|
"description": "Logs a user in. Returns a JWT-Token to authenticate further requests.",
|
||||||
|
@ -2324,6 +2587,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/shares/{share}/auth": {
|
||||||
|
"post": {
|
||||||
|
"description": "Get a jwt auth token for a shared list from a share hash.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sharing"
|
||||||
|
],
|
||||||
|
"summary": "Get an auth token for a share",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The share hash",
|
||||||
|
"name": "share",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The valid jwt auth token.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/v1.Token"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid link share object provided.",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/tasks/all": {
|
"/tasks/all": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -2380,7 +2690,7 @@
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2429,7 +2739,7 @@
|
||||||
"description": "The updated task object.",
|
"description": "The updated task object.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
@ -2489,7 +2799,7 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -2498,7 +2808,7 @@
|
||||||
"description": "The updated task object.",
|
"description": "The updated task object.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
@ -2580,6 +2890,63 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/tasks/{taskID}/assignees": {
|
"/tasks/{taskID}/assignees": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"JWTKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns an array with all assignees for this task.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assignees"
|
||||||
|
],
|
||||||
|
"summary": "Get all assignees for a task",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.",
|
||||||
|
"name": "p",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search assignees by their username.",
|
||||||
|
"name": "s",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Task ID",
|
||||||
|
"name": "taskID",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The assignees",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.Message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"put": {
|
"put": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
@ -2605,7 +2972,7 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTaskAssginee"
|
"$ref": "#/definitions/models.TaskAssginee"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2621,7 +2988,7 @@
|
||||||
"description": "The created assingee object.",
|
"description": "The created assingee object.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTaskAssginee"
|
"$ref": "#/definitions/models.TaskAssginee"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
@ -2683,7 +3050,7 @@
|
||||||
"description": "The created assingees object.",
|
"description": "The created assingees object.",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$ref": "#/definitions/models.ListTaskAssginee"
|
"$ref": "#/definitions/models.TaskAssginee"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
@ -3789,7 +4156,7 @@
|
||||||
"description": "An array of subtasks.",
|
"description": "An array of subtasks.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"task_ids": {
|
"task_ids": {
|
||||||
|
@ -3882,6 +4249,48 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.LinkSharing": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"description": "A unix timestamp when this list was shared. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"description": "The public id to get this shared list",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "The ID of the shared thing",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.List"
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"description": "The right this list is shared with. 0 = Read only, 1 = Read \u0026 Write, 2 = Admin. See the docs for more details.",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 0,
|
||||||
|
"maximum": 2
|
||||||
|
},
|
||||||
|
"shared_by": {
|
||||||
|
"description": "The user who shared this list",
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
},
|
||||||
|
"sharing_type": {
|
||||||
|
"description": "The kind of this link. 0 = undefined, 1 = without password, 2 = with password (currently not implemented).",
|
||||||
|
"type": "integer",
|
||||||
|
"default": 0,
|
||||||
|
"maximum": 2
|
||||||
|
},
|
||||||
|
"updated": {
|
||||||
|
"description": "A unix timestamp when this share was last updated. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.List": {
|
"models.List": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -3906,7 +4315,7 @@
|
||||||
"description": "An array of tasks which belong to the list.",
|
"description": "An array of tasks which belong to the list.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/models.ListTask"
|
"$ref": "#/definitions/models.Task"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
|
@ -3921,118 +4330,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models.ListTask": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"assignees": {
|
|
||||||
"description": "An array of users who are assigned to this task",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.User"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created": {
|
|
||||||
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"createdBy": {
|
|
||||||
"description": "The user who initially created the task.",
|
|
||||||
"type": "object",
|
|
||||||
"$ref": "#/definitions/models.User"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"description": "The task description.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"done": {
|
|
||||||
"description": "Whether a task is done or not.",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"doneAt": {
|
|
||||||
"description": "The unix timestamp when a task was marked as done.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"dueDate": {
|
|
||||||
"description": "A unix timestamp when the task is due.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"endDate": {
|
|
||||||
"description": "When this task ends.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"hexColor": {
|
|
||||||
"description": "The task color in hex",
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 6
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"description": "The unique, numeric id of this task.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"description": "An array of labels which are associated with this task.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.Label"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"listID": {
|
|
||||||
"description": "The list this task belongs to.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"parentTaskID": {
|
|
||||||
"description": "If the task is a subtask, this is the id of its parent.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"priority": {
|
|
||||||
"description": "The task priority. Can be anything you want, it is possible to sort by this later.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"reminderDates": {
|
|
||||||
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repeatAfter": {
|
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"startDate": {
|
|
||||||
"description": "When this task starts.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"subtasks": {
|
|
||||||
"description": "An array of subtasks.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/models.ListTask"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"text": {
|
|
||||||
"description": "The task text. This is what you'll see in the list.",
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 250,
|
|
||||||
"minLength": 3
|
|
||||||
},
|
|
||||||
"updated": {
|
|
||||||
"description": "A unix timestamp when this task was last updated. You cannot change this value.",
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models.ListTaskAssginee": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models.ListUser": {
|
"models.ListUser": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -4188,6 +4485,118 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.Task": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"assignees": {
|
||||||
|
"description": "An array of users who are assigned to this task",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"createdBy": {
|
||||||
|
"description": "The user who initially created the task.",
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/models.User"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"description": "The task description.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"done": {
|
||||||
|
"description": "Whether a task is done or not.",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"doneAt": {
|
||||||
|
"description": "The unix timestamp when a task was marked as done.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"dueDate": {
|
||||||
|
"description": "A unix timestamp when the task is due.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"endDate": {
|
||||||
|
"description": "When this task ends.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"hexColor": {
|
||||||
|
"description": "The task color in hex",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 6
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "The unique, numeric id of this task.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"description": "An array of labels which are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Label"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"listID": {
|
||||||
|
"description": "The list this task belongs to.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"parentTaskID": {
|
||||||
|
"description": "If the task is a subtask, this is the id of its parent.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"description": "The task priority. Can be anything you want, it is possible to sort by this later.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"reminderDates": {
|
||||||
|
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeatAfter": {
|
||||||
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"startDate": {
|
||||||
|
"description": "When this task starts.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"subtasks": {
|
||||||
|
"description": "An array of subtasks.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Task"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"description": "The task text. This is what you'll see in the list.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 250,
|
||||||
|
"minLength": 3
|
||||||
|
},
|
||||||
|
"updated": {
|
||||||
|
"description": "A unix timestamp when this task was last updated. You cannot change this value.",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.TaskAssginee": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Team": {
|
"models.Team": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -4485,6 +4894,9 @@
|
||||||
"frontend_url": {
|
"frontend_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"link_sharing_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"motd": {
|
"motd": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
@ -98,7 +98,7 @@ definitions:
|
||||||
subtasks:
|
subtasks:
|
||||||
description: An array of subtasks.
|
description: An array of subtasks.
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: array
|
type: array
|
||||||
task_ids:
|
task_ids:
|
||||||
description: A list of task ids to update
|
description: A list of task ids to update
|
||||||
|
@ -170,6 +170,42 @@ definitions:
|
||||||
$ref: '#/definitions/models.Label'
|
$ref: '#/definitions/models.Label'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
models.LinkSharing:
|
||||||
|
properties:
|
||||||
|
created:
|
||||||
|
description: A unix timestamp when this list was shared. You cannot change
|
||||||
|
this value.
|
||||||
|
type: integer
|
||||||
|
hash:
|
||||||
|
description: The public id to get this shared list
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
description: The ID of the shared thing
|
||||||
|
type: integer
|
||||||
|
list:
|
||||||
|
$ref: '#/definitions/models.List'
|
||||||
|
type: object
|
||||||
|
right:
|
||||||
|
default: 0
|
||||||
|
description: The right this list is shared with. 0 = Read only, 1 = Read &
|
||||||
|
Write, 2 = Admin. See the docs for more details.
|
||||||
|
maximum: 2
|
||||||
|
type: integer
|
||||||
|
shared_by:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
description: The user who shared this list
|
||||||
|
type: object
|
||||||
|
sharing_type:
|
||||||
|
default: 0
|
||||||
|
description: The kind of this link. 0 = undefined, 1 = without password, 2
|
||||||
|
= with password (currently not implemented).
|
||||||
|
maximum: 2
|
||||||
|
type: integer
|
||||||
|
updated:
|
||||||
|
description: A unix timestamp when this share was last updated. You cannot
|
||||||
|
change this value.
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
models.List:
|
models.List:
|
||||||
properties:
|
properties:
|
||||||
created:
|
created:
|
||||||
|
@ -189,7 +225,7 @@ definitions:
|
||||||
tasks:
|
tasks:
|
||||||
description: An array of tasks which belong to the list.
|
description: An array of tasks which belong to the list.
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: array
|
type: array
|
||||||
title:
|
title:
|
||||||
description: The title of the list. You'll see this in the namespace overview.
|
description: The title of the list. You'll see this in the namespace overview.
|
||||||
|
@ -201,94 +237,6 @@ definitions:
|
||||||
change this value.
|
change this value.
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
models.ListTask:
|
|
||||||
properties:
|
|
||||||
assignees:
|
|
||||||
description: An array of users who are assigned to this task
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/models.User'
|
|
||||||
type: array
|
|
||||||
created:
|
|
||||||
description: A unix timestamp when this task was created. You cannot change
|
|
||||||
this value.
|
|
||||||
type: integer
|
|
||||||
createdBy:
|
|
||||||
$ref: '#/definitions/models.User'
|
|
||||||
description: The user who initially created the task.
|
|
||||||
type: object
|
|
||||||
description:
|
|
||||||
description: The task description.
|
|
||||||
type: string
|
|
||||||
done:
|
|
||||||
description: Whether a task is done or not.
|
|
||||||
type: boolean
|
|
||||||
doneAt:
|
|
||||||
description: The unix timestamp when a task was marked as done.
|
|
||||||
type: integer
|
|
||||||
dueDate:
|
|
||||||
description: A unix timestamp when the task is due.
|
|
||||||
type: integer
|
|
||||||
endDate:
|
|
||||||
description: When this task ends.
|
|
||||||
type: integer
|
|
||||||
hexColor:
|
|
||||||
description: The task color in hex
|
|
||||||
maxLength: 6
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
description: The unique, numeric id of this task.
|
|
||||||
type: integer
|
|
||||||
labels:
|
|
||||||
description: An array of labels which are associated with this task.
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/models.Label'
|
|
||||||
type: array
|
|
||||||
listID:
|
|
||||||
description: The list this task belongs to.
|
|
||||||
type: integer
|
|
||||||
parentTaskID:
|
|
||||||
description: If the task is a subtask, this is the id of its parent.
|
|
||||||
type: integer
|
|
||||||
priority:
|
|
||||||
description: The task priority. Can be anything you want, it is possible to
|
|
||||||
sort by this later.
|
|
||||||
type: integer
|
|
||||||
reminderDates:
|
|
||||||
description: An array of unix timestamps when the user wants to be reminded
|
|
||||||
of the task.
|
|
||||||
items:
|
|
||||||
type: integer
|
|
||||||
type: array
|
|
||||||
repeatAfter:
|
|
||||||
description: An amount in seconds this task repeats itself. If this is set,
|
|
||||||
when marking the task as done, it will mark itself as "undone" and then
|
|
||||||
increase all remindes and the due date by its amount.
|
|
||||||
type: integer
|
|
||||||
startDate:
|
|
||||||
description: When this task starts.
|
|
||||||
type: integer
|
|
||||||
subtasks:
|
|
||||||
description: An array of subtasks.
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/models.ListTask'
|
|
||||||
type: array
|
|
||||||
text:
|
|
||||||
description: The task text. This is what you'll see in the list.
|
|
||||||
maxLength: 250
|
|
||||||
minLength: 3
|
|
||||||
type: string
|
|
||||||
updated:
|
|
||||||
description: A unix timestamp when this task was last updated. You cannot
|
|
||||||
change this value.
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
models.ListTaskAssginee:
|
|
||||||
properties:
|
|
||||||
created:
|
|
||||||
type: integer
|
|
||||||
user_id:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
models.ListUser:
|
models.ListUser:
|
||||||
properties:
|
properties:
|
||||||
created:
|
created:
|
||||||
|
@ -412,6 +360,94 @@ definitions:
|
||||||
maxLength: 250
|
maxLength: 250
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
models.Task:
|
||||||
|
properties:
|
||||||
|
assignees:
|
||||||
|
description: An array of users who are assigned to this task
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
type: array
|
||||||
|
created:
|
||||||
|
description: A unix timestamp when this task was created. You cannot change
|
||||||
|
this value.
|
||||||
|
type: integer
|
||||||
|
createdBy:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
description: The user who initially created the task.
|
||||||
|
type: object
|
||||||
|
description:
|
||||||
|
description: The task description.
|
||||||
|
type: string
|
||||||
|
done:
|
||||||
|
description: Whether a task is done or not.
|
||||||
|
type: boolean
|
||||||
|
doneAt:
|
||||||
|
description: The unix timestamp when a task was marked as done.
|
||||||
|
type: integer
|
||||||
|
dueDate:
|
||||||
|
description: A unix timestamp when the task is due.
|
||||||
|
type: integer
|
||||||
|
endDate:
|
||||||
|
description: When this task ends.
|
||||||
|
type: integer
|
||||||
|
hexColor:
|
||||||
|
description: The task color in hex
|
||||||
|
maxLength: 6
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
description: The unique, numeric id of this task.
|
||||||
|
type: integer
|
||||||
|
labels:
|
||||||
|
description: An array of labels which are associated with this task.
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.Label'
|
||||||
|
type: array
|
||||||
|
listID:
|
||||||
|
description: The list this task belongs to.
|
||||||
|
type: integer
|
||||||
|
parentTaskID:
|
||||||
|
description: If the task is a subtask, this is the id of its parent.
|
||||||
|
type: integer
|
||||||
|
priority:
|
||||||
|
description: The task priority. Can be anything you want, it is possible to
|
||||||
|
sort by this later.
|
||||||
|
type: integer
|
||||||
|
reminderDates:
|
||||||
|
description: An array of unix timestamps when the user wants to be reminded
|
||||||
|
of the task.
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
repeatAfter:
|
||||||
|
description: An amount in seconds this task repeats itself. If this is set,
|
||||||
|
when marking the task as done, it will mark itself as "undone" and then
|
||||||
|
increase all remindes and the due date by its amount.
|
||||||
|
type: integer
|
||||||
|
startDate:
|
||||||
|
description: When this task starts.
|
||||||
|
type: integer
|
||||||
|
subtasks:
|
||||||
|
description: An array of subtasks.
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.Task'
|
||||||
|
type: array
|
||||||
|
text:
|
||||||
|
description: The task text. This is what you'll see in the list.
|
||||||
|
maxLength: 250
|
||||||
|
minLength: 3
|
||||||
|
type: string
|
||||||
|
updated:
|
||||||
|
description: A unix timestamp when this task was last updated. You cannot
|
||||||
|
change this value.
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
models.TaskAssginee:
|
||||||
|
properties:
|
||||||
|
created:
|
||||||
|
type: integer
|
||||||
|
user_id:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
models.Team:
|
models.Team:
|
||||||
properties:
|
properties:
|
||||||
created:
|
created:
|
||||||
|
@ -652,6 +688,8 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
frontend_url:
|
frontend_url:
|
||||||
type: string
|
type: string
|
||||||
|
link_sharing_enabled:
|
||||||
|
type: boolean
|
||||||
motd:
|
motd:
|
||||||
type: string
|
type: string
|
||||||
version:
|
version:
|
||||||
|
@ -678,7 +716,8 @@ info:
|
||||||
paths:
|
paths:
|
||||||
/info:
|
/info:
|
||||||
get:
|
get:
|
||||||
description: Returns the version, frontendurl and motd of Vikunja
|
description: Returns the version, frontendurl, motd and various settings of
|
||||||
|
Vikunja
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
@ -694,14 +733,15 @@ paths:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Returns an array with all assignees for this task.
|
description: Returns all labels which are either created by the user or associated
|
||||||
|
with a task the user has at least read-access to.
|
||||||
parameters:
|
parameters:
|
||||||
- description: The page number. Used for pagination. If not provided, the first
|
- description: The page number. Used for pagination. If not provided, the first
|
||||||
page of results is returned.
|
page of results is returned.
|
||||||
in: query
|
in: query
|
||||||
name: p
|
name: p
|
||||||
type: integer
|
type: integer
|
||||||
- description: Search assignees by their username.
|
- description: Search labels by label text.
|
||||||
in: query
|
in: query
|
||||||
name: s
|
name: s
|
||||||
type: string
|
type: string
|
||||||
|
@ -709,10 +749,10 @@ paths:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The assignees
|
description: The labels
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/models.User'
|
$ref: '#/definitions/models.Label'
|
||||||
type: array
|
type: array
|
||||||
"500":
|
"500":
|
||||||
description: Internal error
|
description: Internal error
|
||||||
|
@ -721,9 +761,9 @@ paths:
|
||||||
type: object
|
type: object
|
||||||
security:
|
security:
|
||||||
- JWTKeyAuth: []
|
- JWTKeyAuth: []
|
||||||
summary: Get all assignees for a task
|
summary: Get all labels a user has access to
|
||||||
tags:
|
tags:
|
||||||
- assignees
|
- labels
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
|
@ -1057,7 +1097,7 @@ paths:
|
||||||
name: task
|
name: task
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: object
|
type: object
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
|
@ -1065,7 +1105,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: The created task object.
|
description: The created task object.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: object
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Invalid task object provided.
|
description: Invalid task object provided.
|
||||||
|
@ -1320,6 +1360,184 @@ paths:
|
||||||
summary: Add a user to a list
|
summary: Add a user to a list
|
||||||
tags:
|
tags:
|
||||||
- sharing
|
- sharing
|
||||||
|
/lists/{list}/shares:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns all link shares which exist for a given list
|
||||||
|
parameters:
|
||||||
|
- description: List ID
|
||||||
|
in: path
|
||||||
|
name: list
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: The page number. Used for pagination. If not provided, the first
|
||||||
|
page of results is returned.
|
||||||
|
in: query
|
||||||
|
name: p
|
||||||
|
type: integer
|
||||||
|
- description: Search shares by hash.
|
||||||
|
in: query
|
||||||
|
name: s
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The share links
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.LinkSharing'
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- JWTKeyAuth: []
|
||||||
|
summary: Get all link shares for a list
|
||||||
|
tags:
|
||||||
|
- sharing
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Share a list via link. The user needs to have write-access to the
|
||||||
|
list to be able do this.
|
||||||
|
parameters:
|
||||||
|
- description: List ID
|
||||||
|
in: path
|
||||||
|
name: list
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: The new link share object
|
||||||
|
in: body
|
||||||
|
name: label
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.LinkSharing'
|
||||||
|
type: object
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The created link share object.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.LinkSharing'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Invalid link share object provided.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"403":
|
||||||
|
description: Not allowed to add the list share.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"404":
|
||||||
|
description: The list does not exist.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- JWTKeyAuth: []
|
||||||
|
summary: Share a list via link
|
||||||
|
tags:
|
||||||
|
- sharing
|
||||||
|
/lists/{list}/shares/{share}:
|
||||||
|
delete:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Remove a link share. The user needs to have write-access to the
|
||||||
|
list to be able do this.
|
||||||
|
parameters:
|
||||||
|
- description: List ID
|
||||||
|
in: path
|
||||||
|
name: list
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Share Link ID
|
||||||
|
in: path
|
||||||
|
name: share
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The link was successfully removed.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
"403":
|
||||||
|
description: Not allowed to remove the link.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"404":
|
||||||
|
description: Share Link not found.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- JWTKeyAuth: []
|
||||||
|
summary: Remove a link share
|
||||||
|
tags:
|
||||||
|
- sharing
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns one link share by its ID.
|
||||||
|
parameters:
|
||||||
|
- description: List ID
|
||||||
|
in: path
|
||||||
|
name: list
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Share ID
|
||||||
|
in: path
|
||||||
|
name: share
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The share links
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.LinkSharing'
|
||||||
|
type: object
|
||||||
|
"403":
|
||||||
|
description: No access to the list
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"404":
|
||||||
|
description: Share Link not found.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- JWTKeyAuth: []
|
||||||
|
summary: Get one link shares for a list
|
||||||
|
tags:
|
||||||
|
- sharing
|
||||||
/lists/{listID}/teams/{teamID}:
|
/lists/{listID}/teams/{teamID}:
|
||||||
delete:
|
delete:
|
||||||
description: Delets a team from a list. The team won't have access to the list
|
description: Delets a team from a list. The team won't have access to the list
|
||||||
|
@ -2232,6 +2450,38 @@ paths:
|
||||||
summary: Register
|
summary: Register
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
/shares/{share}/auth:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Get a jwt auth token for a shared list from a share hash.
|
||||||
|
parameters:
|
||||||
|
- description: The share hash
|
||||||
|
in: path
|
||||||
|
name: share
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The valid jwt auth token.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/v1.Token'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Invalid link share object provided.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
summary: Get an auth token for a share
|
||||||
|
tags:
|
||||||
|
- sharing
|
||||||
/tasks/{id}:
|
/tasks/{id}:
|
||||||
delete:
|
delete:
|
||||||
description: Deletes a task from a list. This does not mean "mark it done".
|
description: Deletes a task from a list. This does not mean "mark it done".
|
||||||
|
@ -2286,7 +2536,7 @@ paths:
|
||||||
name: task
|
name: task
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: object
|
type: object
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
|
@ -2294,7 +2544,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: The updated task object.
|
description: The updated task object.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: object
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Invalid task object provided.
|
description: Invalid task object provided.
|
||||||
|
@ -2452,6 +2702,44 @@ paths:
|
||||||
tags:
|
tags:
|
||||||
- labels
|
- labels
|
||||||
/tasks/{taskID}/assignees:
|
/tasks/{taskID}/assignees:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns an array with all assignees for this task.
|
||||||
|
parameters:
|
||||||
|
- description: The page number. Used for pagination. If not provided, the first
|
||||||
|
page of results is returned.
|
||||||
|
in: query
|
||||||
|
name: p
|
||||||
|
type: integer
|
||||||
|
- description: Search assignees by their username.
|
||||||
|
in: query
|
||||||
|
name: s
|
||||||
|
type: string
|
||||||
|
- description: Task ID
|
||||||
|
in: path
|
||||||
|
name: taskID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The assignees
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.User'
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Message'
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- JWTKeyAuth: []
|
||||||
|
summary: Get all assignees for a task
|
||||||
|
tags:
|
||||||
|
- assignees
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
|
@ -2463,7 +2751,7 @@ paths:
|
||||||
name: assignee
|
name: assignee
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTaskAssginee'
|
$ref: '#/definitions/models.TaskAssginee'
|
||||||
type: object
|
type: object
|
||||||
- description: Task ID
|
- description: Task ID
|
||||||
in: path
|
in: path
|
||||||
|
@ -2476,7 +2764,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: The created assingee object.
|
description: The created assingee object.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTaskAssginee'
|
$ref: '#/definitions/models.TaskAssginee'
|
||||||
type: object
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Invalid assignee object provided.
|
description: Invalid assignee object provided.
|
||||||
|
@ -2559,7 +2847,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: The created assingees object.
|
description: The created assingees object.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTaskAssginee'
|
$ref: '#/definitions/models.TaskAssginee'
|
||||||
type: object
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Invalid assignee object provided.
|
description: Invalid assignee object provided.
|
||||||
|
@ -2659,7 +2947,7 @@ paths:
|
||||||
description: The tasks
|
description: The tasks
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: array
|
type: array
|
||||||
"500":
|
"500":
|
||||||
description: Internal error
|
description: Internal error
|
||||||
|
@ -2693,7 +2981,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: The updated task object.
|
description: The updated task object.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.ListTask'
|
$ref: '#/definitions/models.Task'
|
||||||
type: object
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Invalid task object provided.
|
description: Invalid task object provided.
|
||||||
|
|
Loading…
Reference in a new issue