1
0

Sharing of lists via public links (#94)

This commit is contained in:
konrad
2019-08-31 20:56:41 +00:00
committed by Gitea
parent 88ea66798b
commit 8d57923a7d
41 changed files with 3425 additions and 590 deletions

View File

@ -22,6 +22,29 @@ import (
"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
// =====================
@ -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."}
}
// 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
// ================

View 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

View File

@ -198,4 +198,10 @@
hex_color: f0f0f0
created: 1543626724
updated: 1543626724
- id: 32
text: 'task #32'
created_by_id: 1
list_id: 3
created: 1543626724
updated: 1543626724

View File

@ -131,6 +131,9 @@ func (l *Label) Delete() (err error) {
// @Failure 500 {object} models.Message "Internal error"
// @Router /labels [get]
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()}
@ -192,7 +195,18 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
// Helper method to get all task ids a user has
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 {
return nil, err
}

View File

@ -39,10 +39,19 @@ func (l *Label) CanRead(a web.Auth) (bool, error) {
// CanCreate checks if the user can create a label
// Currently a dummy.
func (l *Label) CanCreate(a web.Auth) (bool, error) {
if _, is := a.(*LinkSharing); is {
return false, nil
}
return true, nil
}
func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
if _, is := a.(*LinkSharing); is {
return false, nil
}
lorig, err := getLabelByIDSimple(l.ID)
if err != nil {
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
func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
// TODO: add an extra check for link share handling
// Get all tasks
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()})
if err != nil {

207
pkg/models/link_sharing.go Normal file
View 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
}

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

View File

@ -85,14 +85,26 @@ func GetListsByNamespaceID(nID int64, doer *User) (lists []*List, err error) {
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists [get]
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)
if err != nil {
return nil, err
}
// Add more list details
AddListDetails(lists)
err = AddListDetails(lists)
return lists, err
}

View File

@ -31,6 +31,13 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
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
if originalList.isOwner(&User{ID: a.GetID()}) {
return true, nil
@ -45,6 +52,14 @@ func (l *List) CanRead(a web.Auth) (bool, error) {
if err := l.GetSimpleByID(); err != nil {
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()}) {
return true, nil
}
@ -61,7 +76,7 @@ func (l *List) CanDelete(a web.Auth) (bool, error) {
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) {
// A user can create a list if he has write access to the namespace
n := &Namespace{ID: l.NamespaceID}
@ -76,6 +91,12 @@ func (l *List) IsAdmin(a web.Auth) (bool, error) {
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 if the user is either owner or can write to the list
// Owners are always admins

View File

@ -22,20 +22,25 @@ import (
// CanCreate checks if the user can create a new user <-> list relation
func (lu *ListUser) CanCreate(a web.Auth) (bool, error) {
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
return l.CanWrite(a)
return lu.canDoListUser(a)
}
// CanDelete checks if the user can delete a user <-> list relation
func (lu *ListUser) CanDelete(a web.Auth) (bool, error) {
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
return l.CanWrite(a)
return lu.canDoListUser(a)
}
// CanUpdate checks if the user can update a user <-> list relation
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
l := List{ID: lu.ListID}
return l.CanWrite(a)

View File

@ -48,6 +48,7 @@ func GetTables() []interface{} {
&Label{},
&LabelTask{},
&TaskReminder{},
&LinkSharing{},
}
}

View File

@ -138,6 +138,10 @@ type NamespaceWithLists struct {
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces [get]
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)
if err != nil {
return nil, err

View File

@ -48,12 +48,21 @@ func (n *Namespace) CanDelete(a web.Auth) (bool, error) {
// CanCreate checks if the user can create a new namespace
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.
return true, nil
}
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
err := n.GetSimpleByID()
if err != nil {

View File

@ -221,13 +221,27 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
// @Produce json
// @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 taskID path int true "Task ID"
// @Security JWTKeyAuth
// @Success 200 {array} models.User "The assignees"
// @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) {
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
err := x.Table("task_assignees").
err = x.Table("task_assignees").
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").

View File

@ -41,5 +41,5 @@ func canDoTaskAssingee(taskID int64, a web.Auth) (bool, error) {
if err != nil {
return false, err
}
return list.CanCreate(a)
return list.CanUpdate(a)
}

View File

@ -317,6 +317,15 @@ func sortTasksForTesting(by SortBy) (tasks []*Task) {
Created: 1543626724,
Updated: 1543626724,
},
{
ID: 32,
Text: "task #32",
CreatedByID: 1,
CreatedBy: user1,
ListID: 3,
Created: 1543626724,
Updated: 1543626724,
},
}
switch by {

View File

@ -147,17 +147,40 @@ func (t *Task) ReadAll(search string, a web.Auth, page int) (interface{}, error)
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
func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate time.Time, endDate time.Time) ([]*Task, error) {
// Get all lists
lists, err := getRawListsForUser("", u, page)
shareAuth, is := a.(*LinkSharing)
if is {
shareAuth.List = &List{ID: shareAuth.ListID}
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 {
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
var listIDs []int64
for _, l := range lists {
@ -165,7 +188,7 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
}
var orderby string
switch sortby {
switch opts.sortby {
case SortTasksByPriorityDesc:
orderby = "priority desc"
case SortTasksByPriorityAsc:
@ -179,20 +202,20 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate t
taskMap := make(map[int64]*Task)
// 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()
if startDate.Unix() != 0 {
startDateUnix = startDate.Unix()
if opts.startDate.Unix() != 0 {
startDateUnix = opts.startDate.Unix()
}
endDateUnix := time.Now().Unix()
if endDate.Unix() != 0 {
endDateUnix = endDate.Unix()
if opts.endDate.Unix() != 0 {
endDateUnix = opts.endDate.Unix()
}
if err := x.In("list_id", listIDs).
Where("text LIKE ?", "%"+search+"%").
Where("text LIKE ?", "%"+opts.search+"%").
And("((due_date_unix BETWEEN ? AND ?) OR "+
"(start_date_unix BETWEEN ? and ?) OR "+
"(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 {
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)").
OrderBy(orderby).
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 {
return nil, err
}
// 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.
sortTasks(tasks, sortby)
sortTasks(tasks, opts.sortby)
return tasks, err
}

View File

@ -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
list := &List{ID: t.ListID}
return list.CanRead(a)
l := &List{ID: t.ListID}
return l.CanRead(a)
}
// Helper function to check if a user can do stuff on a list task

View File

@ -36,6 +36,11 @@ func (tl *TeamList) CanUpdate(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}
return l.IsAdmin(a)
}

View File

@ -32,6 +32,11 @@ func (tm *TeamMember) CanDelete(a web.Auth) (bool, error) {
// IsAdmin checks if the user is team admin
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
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", a.GetID(), tm.TeamID, true).
Get(&TeamMember{})

View File

@ -142,6 +142,10 @@ func (t *Team) ReadOne() (err error) {
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams [get]
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
if _, is := a.(*LinkSharing); is {
return nil, ErrGenericForbidden{}
}
all := []*Team{}
err := x.Select("teams.*").
Table("teams").

View File

@ -22,6 +22,10 @@ import (
// CanCreate checks if the user can create a new team
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.
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
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
_, err := GetTeamByID(t.ID)
if err != nil {

View File

@ -189,6 +189,11 @@ func CheckUserCredentials(u *UserLogin) (*User, error) {
func GetCurrentUser(c echo.Context) (user *User, err error) {
jwtinf := c.Get("user").(*jwt.Token)
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)
if !ok {
return user, ErrCouldNotGetUserID{}