Merge pull request #1042 from ramiresviana/fixes-2

pull/1044/head
Oleg Lobanov 2020-07-23 15:03:10 +02:00 committed by GitHub
commit 14e2f84ceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 25 deletions

View File

@ -16,4 +16,5 @@ var (
ErrInvalidAuthMethod = errors.New("invalid auth method") ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrPermissionDenied = errors.New("permission denied") ErrPermissionDenied = errors.New("permission denied")
ErrInvalidRequestParams = errors.New("invalid request params") ErrInvalidRequestParams = errors.New("invalid request params")
ErrSourceIsParent = errors.New("source is parent")
) )

View File

@ -43,7 +43,7 @@ async function resourceAction (url, method, content) {
const res = await fetchURL(`/api/resources${url}`, opts) const res = await fetchURL(`/api/resources${url}`, opts)
if (res.status !== 200) { if (res.status !== 200) {
throw new Error(res.responseText) throw new Error(await res.text())
} else { } else {
return res return res
} }

View File

@ -8,9 +8,7 @@
<input style="display:none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory multiple> <input style="display:none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory multiple>
</div> </div>
<div v-else id="listing" <div v-else id="listing"
:class="user.viewMode" :class="user.viewMode">
@dragenter="dragEnter"
@dragend="dragEnd">
<div> <div>
<div class="item header"> <div class="item header">
<div></div> <div></div>
@ -99,7 +97,8 @@ export default {
components: { Item }, components: { Item },
data: function () { data: function () {
return { return {
showLimit: 50 showLimit: 50,
dragCounter: 0
} }
}, },
computed: { computed: {
@ -171,6 +170,8 @@ export default {
window.addEventListener('resize', this.resizeEvent) window.addEventListener('resize', this.resizeEvent)
window.addEventListener('scroll', this.scrollEvent) window.addEventListener('scroll', this.scrollEvent)
document.addEventListener('dragover', this.preventDefault) document.addEventListener('dragover', this.preventDefault)
document.addEventListener('dragenter', this.dragEnter)
document.addEventListener('dragleave', this.dragLeave)
document.addEventListener('drop', this.drop) document.addEventListener('drop', this.drop)
}, },
beforeDestroy () { beforeDestroy () {
@ -179,6 +180,8 @@ export default {
window.removeEventListener('resize', this.resizeEvent) window.removeEventListener('resize', this.resizeEvent)
window.removeEventListener('scroll', this.scrollEvent) window.removeEventListener('scroll', this.scrollEvent)
document.removeEventListener('dragover', this.preventDefault) document.removeEventListener('dragover', this.preventDefault)
document.removeEventListener('dragenter', this.dragEnter)
document.removeEventListener('dragleave', this.dragLeave)
document.removeEventListener('drop', this.drop) document.removeEventListener('drop', this.drop)
}, },
methods: { methods: {
@ -326,6 +329,8 @@ export default {
} }
}, },
dragEnter () { dragEnter () {
this.dragCounter++
// When the user starts dragging an item, put every // When the user starts dragging an item, put every
// file on the listing with 50% opacity. // file on the listing with 50% opacity.
let items = document.getElementsByClassName('item') let items = document.getElementsByClassName('item')
@ -334,11 +339,16 @@ export default {
file.style.opacity = 0.5 file.style.opacity = 0.5
}) })
}, },
dragEnd () { dragLeave () {
this.resetOpacity() this.dragCounter--
if (this.dragCounter == 0) {
this.resetOpacity()
}
}, },
drop: async function (event) { drop: async function (event) {
event.preventDefault() event.preventDefault()
this.dragCounter = 0
this.resetOpacity() this.resetOpacity()
let dt = event.dataTransfer let dt = event.dataTransfer
@ -408,12 +418,13 @@ export default {
confirm: (event) => { confirm: (event) => {
event.preventDefault() event.preventDefault()
this.$store.commit('closeHovers') this.$store.commit('closeHovers')
this.handleFiles(files, path, true) upload.handleFiles(files, path, true)
} }
}) })
return return
} }
upload.handleFiles(files, path) upload.handleFiles(files, path)
}, },
resetOpacity () { resetOpacity () {

View File

@ -27,9 +27,11 @@ export default {
name: 'upload', name: 'upload',
methods: { methods: {
uploadFile: function () { uploadFile: function () {
document.getElementById('upload-input').value = ''
document.getElementById('upload-input').click() document.getElementById('upload-input').click()
}, },
uploadFolder: function () { uploadFolder: function () {
document.getElementById('upload-folder-input').value = ''
document.getElementById('upload-folder-input').click() document.getElementById('upload-folder-input').click()
} }
} }

View File

@ -10,6 +10,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/spf13/afero"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/fileutils" "github.com/filebrowser/filebrowser/v2/fileutils"
@ -139,38 +141,25 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
dst := r.URL.Query().Get("destination") dst := r.URL.Query().Get("destination")
action := r.URL.Query().Get("action") action := r.URL.Query().Get("action")
dst, err := url.QueryUnescape(dst) dst, err := url.QueryUnescape(dst)
if err != nil { if err != nil {
return errToStatus(err), err return errToStatus(err), err
} }
if dst == "/" || src == "/" { if dst == "/" || src == "/" {
return http.StatusForbidden, nil return http.StatusForbidden, nil
} }
if err = checkParent(src, dst); err != nil {
return http.StatusBadRequest, err
}
override := r.URL.Query().Get("override") == "true" override := r.URL.Query().Get("override") == "true"
rename := r.URL.Query().Get("rename") == "true" rename := r.URL.Query().Get("rename") == "true"
if !override && !rename { if !override && !rename {
if _, err = d.user.Fs.Stat(dst); err == nil { if _, err = d.user.Fs.Stat(dst); err == nil {
return http.StatusConflict, nil return http.StatusConflict, nil
} }
} }
if rename { if rename {
counter := 1 dst = addVersionSuffix(dst, d.user.Fs)
dir, name := filepath.Split(dst)
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
for {
if _, err = d.user.Fs.Stat(dst); err != nil {
break
}
new := fmt.Sprintf("%s(%d)%s", base, counter, ext)
dst = filepath.Join(dir, new)
counter++
}
} }
err = d.RunHook(func() error { err = d.RunHook(func() error {
@ -180,11 +169,14 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
if !d.user.Perm.Create { if !d.user.Perm.Create {
return errors.ErrPermissionDenied return errors.ErrPermissionDenied
} }
return fileutils.Copy(d.user.Fs, src, dst) return fileutils.Copy(d.user.Fs, src, dst)
case "rename": case "rename":
if !d.user.Perm.Rename { if !d.user.Perm.Rename {
return errors.ErrPermissionDenied return errors.ErrPermissionDenied
} }
dst = filepath.Clean("/" + dst)
return d.user.Fs.Rename(src, dst) return d.user.Fs.Rename(src, dst)
default: default:
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams) return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
@ -193,3 +185,35 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
return errToStatus(err), err return errToStatus(err), err
}) })
func checkParent(src, dst string) error {
rel, err := filepath.Rel(src, dst)
if err != nil {
return err
}
rel = filepath.ToSlash(rel)
if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
return errors.ErrSourceIsParent
}
return nil
}
func addVersionSuffix(path string, fs afero.Fs) string {
counter := 1
dir, name := filepath.Split(path)
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
for {
if _, err := fs.Stat(path); err != nil {
break
}
renamed := fmt.Sprintf("%s(%d)%s", base, counter, ext)
path = filepath.ToSlash(dir) + renamed
counter++
}
return path
}