Fix upload

This commit is contained in:
kolaente 2020-12-23 02:41:44 +01:00
parent 39e11fcb2b
commit 7634498c1c
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
5 changed files with 131 additions and 22 deletions

View File

@ -19,6 +19,7 @@ package background
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web"
"xorm.io/xorm"
)
// Image represents an image which can be used as a list background
@ -33,7 +34,7 @@ type Image struct {
// Provider represents something that is able to get a list of images and set one of them as background
type Provider interface {
// Search is used to either return a pre-defined list of Image or let the user search for an image
Search(search string, page int64) (result []*Image, err error)
Search(s *xorm.Session, search string, page int64) (result []*Image, err error)
// Set sets an image which was most likely previously obtained by Search as list background
Set(image *Image, list *models.List, auth web.Auth) (err error)
Set(s *xorm.Session, image *Image, list *models.List, auth web.Auth) (err error)
}

View File

@ -17,10 +17,12 @@
package handler
import (
"code.vikunja.io/api/pkg/db"
"io"
"net/http"
"strconv"
"strings"
"xorm.io/xorm"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log"
@ -59,8 +61,17 @@ func (bp *BackgroundProvider) SearchBackgrounds(c echo.Context) error {
}
}
result, err := p.Search(search, page)
s := db.NewSession()
defer s.Close()
result, err := p.Search(s, search, page)
if err != nil {
_ = s.Rollback()
return echo.NewHTTPError(http.StatusBadRequest, "An error occurred: "+err.Error())
}
if err := s.Commit(); err != nil {
_ = s.Rollback()
return echo.NewHTTPError(http.StatusBadRequest, "An error occurred: "+err.Error())
}
@ -68,7 +79,7 @@ func (bp *BackgroundProvider) SearchBackgrounds(c echo.Context) error {
}
// This function does all kinds of preparations for setting and uploading a background
func (bp *BackgroundProvider) setBackgroundPreparations(c echo.Context) (list *models.List, auth web.Auth, err error) {
func (bp *BackgroundProvider) setBackgroundPreparations(s *xorm.Session, c echo.Context) (list *models.List, auth web.Auth, err error) {
auth, err = auth2.GetAuthFromClaims(c)
if err != nil {
return nil, nil, echo.NewHTTPError(http.StatusBadRequest, "Invalid auth token: "+err.Error())
@ -96,8 +107,12 @@ func (bp *BackgroundProvider) setBackgroundPreparations(c echo.Context) (list *m
// SetBackground sets an Image as list background
func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
list, auth, err := bp.setBackgroundPreparations(c)
s := db.NewSession()
defer s.Close()
list, auth, err := bp.setBackgroundPreparations(s, c)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
@ -106,11 +121,13 @@ func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
image := &background.Image{}
err = c.Bind(image)
if err != nil {
_ = s.Rollback()
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided: "+err.Error())
}
err = p.Set(image, list, auth)
err = p.Set(s, image, list, auth)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, list)
@ -118,8 +135,12 @@ func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
// UploadBackground uploads a background and passes the id of the uploaded file as an Image to the Set function of the BackgroundProvider.
func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
list, auth, err := bp.setBackgroundPreparations(c)
s := db.NewSession()
defer s.Close()
list, auth, err := bp.setBackgroundPreparations(s, c)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
@ -128,10 +149,12 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
// Get + upload the image
file, err := c.FormFile("background")
if err != nil {
_ = s.Rollback()
return err
}
src, err := file.Open()
if err != nil {
_ = s.Rollback()
return err
}
defer src.Close()
@ -139,9 +162,11 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
// Validate we're dealing with an image
mime, err := mimetype.DetectReader(src)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
if !strings.HasPrefix(mime.String(), "image") {
_ = s.Rollback()
return c.JSON(http.StatusBadRequest, models.Message{Message: "Uploaded file is no image."})
}
_, _ = src.Seek(0, io.SeekStart)
@ -149,6 +174,7 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
// Save the file
f, err := files.CreateWithMime(src, file.Filename, uint64(file.Size), auth, mime.String())
if err != nil {
_ = s.Rollback()
if files.IsErrFileIsTooLarge(err) {
return echo.ErrBadRequest
}
@ -158,10 +184,17 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
image := &background.Image{ID: strconv.FormatInt(f.ID, 10)}
err = p.Set(image, list, auth)
err = p.Set(s, image, list, auth)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
if err := s.Commit(); err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, list)
}
@ -190,17 +223,23 @@ func GetListBackground(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid list ID: "+err.Error())
}
s := db.NewSession()
defer s.Close()
// Check if a background for this list exists + Rights
list := &models.List{ID: listID}
can, _, err := list.CanRead(nil, auth)
can, _, err := list.CanRead(s, auth)
if err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
if !can {
_ = s.Rollback()
log.Infof("Tried to get list background of list %d while not having the rights for it (User: %v)", listID, auth)
return echo.NewHTTPError(http.StatusForbidden)
}
if list.BackgroundFileID == 0 {
_ = s.Rollback()
return echo.NotFoundHandler(c)
}
@ -209,13 +248,19 @@ func GetListBackground(c echo.Context) error {
ID: list.BackgroundFileID,
}
if err := bgFile.LoadFileByID(); err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
// Unsplash requires pingbacks as per their api usage guidelines.
// To do this in a privacy-preserving manner, we do the ping from inside of Vikunja to not expose any user details.
// FIXME: This should use an event once we have events
unsplash.Pingback(bgFile)
unsplash.Pingback(s, bgFile)
if err := s.Commit(); err != nil {
_ = s.Rollback()
return handler.HandleHTTPError(err, c)
}
// Serve the file
return c.Stream(http.StatusOK, "image/jpg", bgFile.File)

View File

@ -25,6 +25,7 @@ import (
"strconv"
"strings"
"time"
"xorm.io/xorm"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
@ -150,7 +151,7 @@ func getUnsplashPhotoInfoByID(photoID string) (photo *Photo, err error) {
// @Success 200 {array} background.Image "An array with photos"
// @Failure 500 {object} models.Message "Internal error"
// @Router /backgrounds/unsplash/search [get]
func (p *Provider) Search(search string, page int64) (result []*background.Image, err error) {
func (p *Provider) Search(s *xorm.Session, search string, page int64) (result []*background.Image, err error) {
// If we don't have a search query, return results from the unsplash featured collection
if search == "" {
@ -243,7 +244,7 @@ func (p *Provider) Search(search string, page int64) (result []*background.Image
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/backgrounds/unsplash [post]
func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth) (err error) {
func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.List, auth web.Auth) (err error) {
// Find the photo
photo, err := getUnsplashPhotoInfoByID(image.ID)
@ -315,13 +316,13 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
list.BackgroundInformation = unsplashPhoto
// Set it as the list background
return models.SetListBackground(list.ID, file)
return models.SetListBackground(s, list.ID, file)
}
// Pingback pings the unsplash api if an unsplash photo has been accessed.
func Pingback(f *files.File) {
func Pingback(s *xorm.Session, f *files.File) {
// Check if the file is actually downloaded from unsplash
unsplashPhoto, err := models.GetUnsplashPhotoByFileID(f.ID)
unsplashPhoto, err := models.GetUnsplashPhotoByFileID(s, f.ID)
if err != nil {
if files.IsErrFileIsNotUnsplashFile(err) {
return

View File

@ -18,6 +18,7 @@ package upload
import (
"strconv"
"xorm.io/xorm"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/models"
@ -30,7 +31,7 @@ type Provider struct {
}
// Search is only used to implement the interface
func (p *Provider) Search(search string, page int64) (result []*background.Image, err error) {
func (p *Provider) Search(s *xorm.Session, search string, page int64) (result []*background.Image, err error) {
return
}
@ -50,7 +51,7 @@ func (p *Provider) Search(search string, page int64) (result []*background.Image
// @Failure 404 {object} models.Message "The list does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/backgrounds/upload [put]
func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth) (err error) {
func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.List, auth web.Auth) (err error) {
// Remove the old background if one exists
if list.BackgroundFileID != 0 {
file := files.File{ID: list.BackgroundFileID}
@ -67,5 +68,5 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
list.BackgroundInformation = &models.ListBackgroundType{Type: models.ListBackgroundUpload}
return models.SetListBackground(list.ID, file)
return models.SetListBackground(s, list.ID, file)
}

View File

@ -17,6 +17,7 @@
package caldav
import (
"code.vikunja.io/api/pkg/db"
"strconv"
"strings"
"time"
@ -90,9 +91,16 @@ func (vcls *VikunjaCaldavListStorage) GetResources(rpath string, withChildren bo
return []data.Resource{r}, nil
}
s := db.NewSession()
defer s.Close()
// Otherwise get all lists
thelists, _, _, err := vcls.list.ReadAll(vcls.user, "", -1, 50)
thelists, _, _, err := vcls.list.ReadAll(s, vcls.user, "", -1, 50)
if err != nil {
_ = s.Rollback()
return nil, err
}
if err := s.Commit(); err != nil {
return nil, err
}
lists := thelists.([]*models.List)
@ -125,10 +133,17 @@ func (vcls *VikunjaCaldavListStorage) GetResourcesByList(rpaths []string) ([]dat
uids = append(uids, string(uid[:endlen]))
}
s := db.NewSession()
defer s.Close()
// GetTasksByUIDs...
// Parse these into ressources...
tasks, err := models.GetTasksByUIDs(s, uids)
if err != nil {
_ = s.Rollback()
return nil, err
}
if err := s.Commit(); err != nil {
return nil, err
}
@ -187,15 +202,22 @@ func (vcls *VikunjaCaldavListStorage) GetResource(rpath string) (*data.Resource,
// If the task is not nil, we need to get the task and not the list
if vcls.task != nil {
s := db.NewSession()
defer s.Close()
// save and override the updated unix date to not break any later etag checks
updated := vcls.task.Updated
task, err := models.GetTaskSimple(s, &models.Task{ID: vcls.task.ID, UID: vcls.task.UID})
if err != nil {
_ = s.Rollback()
if models.IsErrTaskDoesNotExist(err) {
return nil, false, errs.ResourceNotFoundError
}
return nil, false, err
}
if err := s.Commit(); err != nil {
return nil, false, err
}
vcls.task = &task
if updated.Unix() > 0 {
@ -230,6 +252,9 @@ func (vcls *VikunjaCaldavListStorage) GetShallowResource(rpath string) (*data.Re
// CreateResource creates a new resource
func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*data.Resource, error) {
s := db.NewSession()
defer s.Close()
vTask, err := parseTaskFromVTODO(content)
if err != nil {
return nil, err
@ -249,6 +274,11 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
// Create the task
err = vTask.Create(s, vcls.user)
if err != nil {
_ = s.Rollback()
return nil, err
}
if err := s.Commit(); err != nil {
return nil, err
}
@ -272,18 +302,28 @@ func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*da
// At this point, we already have the right task in vcls.task, so we can use that ID directly
vTask.ID = vcls.task.ID
s := db.NewSession()
defer s.Close()
// Check the rights
canUpdate, err := vTask.CanUpdate(s, vcls.user)
if err != nil {
_ = s.Rollback()
return nil, err
}
if !canUpdate {
_ = s.Rollback()
return nil, errs.ForbiddenError
}
// Update the task
err = vTask.Update(s)
if err != nil {
_ = s.Rollback()
return nil, err
}
if err := s.Commit(); err != nil {
return nil, err
}
@ -299,9 +339,13 @@ func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*da
// DeleteResource deletes a resource
func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
if vcls.task != nil {
s := db.NewSession()
defer s.Close()
// Check the rights
canDelete, err := vcls.task.CanDelete(s, vcls.user)
if err != nil {
_ = s.Rollback()
return err
}
if !canDelete {
@ -309,7 +353,13 @@ func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
}
// Delete it
return vcls.task.Delete(s)
err = vcls.task.Delete(s)
if err != nil {
_ = s.Rollback()
return err
}
return s.Commit()
}
return nil
@ -385,16 +435,22 @@ func (vlra *VikunjaListResourceAdapter) GetModTime() time.Time {
}
func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr VikunjaListResourceAdapter, err error) {
can, _, err := vcls.list.CanRead(nil, vcls.user)
s := db.NewSession()
defer s.Close()
can, _, err := vcls.list.CanRead(s, vcls.user)
if err != nil {
_ = s.Rollback()
return
}
if !can {
_ = s.Rollback()
log.Errorf("User %v tried to access a caldav resource (List %v) which they are not allowed to access", vcls.user.Username, vcls.list.ID)
return rr, models.ErrUserDoesNotHaveAccessToList{ListID: vcls.list.ID}
}
err = vcls.list.ReadOne()
err = vcls.list.ReadOne(s)
if err != nil {
_ = s.Rollback()
return
}
@ -405,6 +461,7 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
}
iface, _, _, err := tk.ReadAll(s, vcls.user, "", 1, 1000)
if err != nil {
_ = s.Rollback()
return rr, err
}
tasks, ok := iface.([]*models.Task)
@ -416,6 +473,10 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
vcls.list.Tasks = tasks
}
if err := s.Commit(); err != nil {
return rr, err
}
rr = VikunjaListResourceAdapter{
list: vcls.list,
listTasks: listTasks,