diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 549bd136..00000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine:latest as alpine -RUN apk --update add ca-certificates -RUN apk --update add mailcap - -FROM scratch -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=alpine /etc/mime.types /etc/mime.types - -VOLUME /srv -EXPOSE 80 - -COPY .docker.json /.filebrowser.json -COPY filebrowser /filebrowser - -ENTRYPOINT [ "/filebrowser" ] diff --git a/Dockerfile b/Dockerfile new file mode 120000 index 00000000..d537b9a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +Dockerfile.debian \ No newline at end of file diff --git a/Dockerfile.alpine b/Dockerfile.alpine index e5d35107..5e715739 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,4 +1,5 @@ FROM alpine:latest as alpine +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk --update add ca-certificates RUN apk --update add mailcap diff --git a/cmd/root.go b/cmd/root.go index 75506f78..4f6de799 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -47,6 +47,7 @@ func init() { flags.Bool("noauth", false, "use the noauth auther when using quick setup") flags.String("username", "admin", "username for the first user when using quick config") flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")") + flags.String("salt", "", "The salt to use when for hashing share password. Can be any value. If changed, existing password-protected shares wil stop working.") //nolint:lll addServerFlags(flags) } @@ -250,6 +251,10 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { _, disableExec := getParamB(flags, "disable-exec") server.EnableExec = !disableExec + if val, set := getParamB(flags, "salt"); set { + server.Salt = val + } + return server } diff --git a/files/file.go b/files/file.go index fa102049..2ccac0da 100644 --- a/files/file.go +++ b/files/file.go @@ -26,28 +26,30 @@ import ( // FileInfo describes a file. type FileInfo struct { *Listing - Fs afero.Fs `json:"-"` - Path string `json:"path"` - Name string `json:"name"` - Size int64 `json:"size"` - Extension string `json:"extension"` - ModTime time.Time `json:"modified"` - Mode os.FileMode `json:"mode"` - IsDir bool `json:"isDir"` - Type string `json:"type"` - Subtitles []string `json:"subtitles,omitempty"` - Content string `json:"content,omitempty"` - Checksums map[string]string `json:"checksums,omitempty"` + Fs afero.Fs `json:"-"` + Path string `json:"path"` + Name string `json:"name"` + Size int64 `json:"size"` + Extension string `json:"extension"` + ModTime time.Time `json:"modified"` + Mode os.FileMode `json:"mode"` + IsDir bool `json:"isDir"` + Type string `json:"type"` + Subtitles []string `json:"subtitles,omitempty"` + Content string `json:"content,omitempty"` + Checksums map[string]string `json:"checksums,omitempty"` + SharedCodeToken string `json:"sharedCodeToken,omitempty"` } // FileOptions are the options when getting a file info. type FileOptions struct { - Fs afero.Fs - Path string - Modify bool - Expand bool - ReadHeader bool - Checker rules.Checker + Fs afero.Fs + Path string + Modify bool + Expand bool + ReadHeader bool + SharedCodeToken string + Checker rules.Checker } // NewFileInfo creates a File object from a path and a given user. This File @@ -64,14 +66,15 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { } file := &FileInfo{ - Fs: opts.Fs, - Path: opts.Path, - Name: info.Name(), - ModTime: info.ModTime(), - Mode: info.Mode(), - IsDir: info.IsDir(), - Size: info.Size(), - Extension: filepath.Ext(info.Name()), + Fs: opts.Fs, + Path: opts.Path, + Name: info.Name(), + ModTime: info.ModTime(), + Mode: info.Mode(), + IsDir: info.IsDir(), + Size: info.Size(), + Extension: filepath.Ext(info.Name()), + SharedCodeToken: opts.SharedCodeToken, } if opts.Expand { diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js index 6f8a2a17..19c4a334 100644 --- a/frontend/src/api/share.js +++ b/frontend/src/api/share.js @@ -4,8 +4,10 @@ export async function list() { return fetchJSON('/api/shares') } -export async function getHash(hash) { - return fetchJSON(`/api/public/share/${hash}`) +export async function getHash(hash, shared_code = "") { + return fetchJSON(`/api/public/share/${hash}`, { + headers: { 'X-SHARED-CODE': shared_code }, + }) } export async function get(url) { @@ -23,14 +25,24 @@ export async function remove(hash) { } } -export async function create(url, expires = '', unit = 'hours') { +export async function create(url, shared_code = '', expires = '', unit = 'hours') { url = removePrefix(url) url = `/api/share${url}` - if (expires !== '') { - url += `?expires=${expires}&unit=${unit}` + if (shared_code !== '' || expires !== '') { + url += '?' + var params = '' + if (expires !== '') { + params += `expires=${expires}&unit=${unit}` + } + if (shared_code !== '') { + if (params != '') { + params += "&" + } + params += `shared_code=${shared_code}` + } + url += params } - return fetchJSON(url, { - method: 'POST' + method: 'POST', }) } diff --git a/frontend/src/components/prompts/Share.vue b/frontend/src/components/prompts/Share.vue index 456b51e3..bfe2e665 100644 --- a/frontend/src/components/prompts/Share.vue +++ b/frontend/src/components/prompts/Share.vue @@ -1,167 +1,220 @@ diff --git a/frontend/src/css/_share.css b/frontend/src/css/_share.css index 967d91d5..150a5230 100644 --- a/frontend/src/css/_share.css +++ b/frontend/src/css/_share.css @@ -62,4 +62,12 @@ .share__box__items #listing.list .item .modified { width: 25%; +} + +.share_wrong { + background: var(--red); + color: #fff; + padding: .5em; + text-align: center; + animation: .2s opac forwards; } \ No newline at end of file diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 5c3a451f..0f094086 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -19,6 +19,7 @@ "permalink": "Get Permanent Link", "previous": "Previous", "publish": "Publish", + "password": "Password", "rename": "Rename", "replace": "Replace", "reportIssue": "Report Issue", @@ -29,10 +30,13 @@ "selectMultiple": "Select multiple", "share": "Share", "shell": "Toggle shell", + "submit": "Submit", "switchView": "Switch view", "toggleSidebar": "Toggle sidebar", "update": "Update", - "upload": "Upload" + "upload": "Upload", + "sharedCode": "Shared code", + "optionalSharedCode": "Shared code(optional)" }, "download": { "downloadFile": "Download File", @@ -142,7 +146,10 @@ "show": "Show", "size": "Size", "upload": "Upload", - "uploadMessage": "Select an option to upload." + "uploadMessage": "Select an option to upload.", + "downloadPrompts": "Download url: {0}", + "downloadPromptsWithSharedCode": "Download url: {0} Shared code: {1}", + "invalidSharedCode": "Invalid shared code." }, "search": { "images": "Images", diff --git a/frontend/src/i18n/zh-cn.json b/frontend/src/i18n/zh-cn.json index ddd021bf..88b212d2 100644 --- a/frontend/src/i18n/zh-cn.json +++ b/frontend/src/i18n/zh-cn.json @@ -13,7 +13,7 @@ "more": "更多", "move": "移动", "moveFile": "移动文件", - "new": "新", + "new": "新建", "next": "下一个", "ok": "确定", "permalink": "获取永久链接", @@ -32,7 +32,9 @@ "switchView": "切换显示方式", "toggleSidebar": "切换侧边栏", "update": "更新", - "upload": "上传" + "upload": "上传", + "sharedCode": "提取码", + "optionalSharedCode": "提取码(可选)" }, "download": { "downloadFile": "下载文件", @@ -142,7 +144,10 @@ "show": "点击以显示", "size": "大小", "upload": "上传", - "uploadMessage": "选择上传项。" + "uploadMessage": "选择上传项。", + "downloadPrompts": "下载链接:{0}", + "downloadPromptsWithSharedCode": "下载链接:{0} 提取码:{1}", + "invalidSharedCode": "提取码无效!" }, "search": { "images": "图像", diff --git a/frontend/src/main.js b/frontend/src/main.js index 520bb8d7..0e03587a 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -2,7 +2,7 @@ import { sync } from 'vuex-router-sync' import store from '@/store' import router from '@/router' import i18n from '@/i18n' -import Vue from '@/utils/vue' +import Vue from 'vue' import { recaptcha, loginPage } from '@/utils/constants' import { login, validateLogin } from '@/utils/auth' import App from '@/App' diff --git a/frontend/src/views/Share.vue b/frontend/src/views/Share.vue index 0cfea28d..39594419 100644 --- a/frontend/src/views/Share.vue +++ b/frontend/src/views/Share.vue @@ -1,45 +1,61 @@ diff --git a/go.mod b/go.mod index 7075fa31..54260c4f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/filebrowser/filebrowser/v2 require ( github.com/DataDog/zstd v1.4.0 // indirect - github.com/GeertJohan/go.rice v1.0.0 + github.com/GeertJohan/go.rice v1.0.2 github.com/Sereal/Sereal v0.0.0-20190430203904-6faf9605eb56 // indirect github.com/asdine/storm v2.1.2+incompatible github.com/caddyserver/caddy v1.0.3 @@ -10,6 +10,7 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/disintegration/imaging v1.6.2 github.com/dsnet/compress v0.0.1 // indirect + github.com/golang/protobuf v1.3.3 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.1 @@ -33,11 +34,11 @@ require ( golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect - golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect golang.org/x/text v0.3.2 // indirect google.golang.org/appengine v1.5.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/yaml.v2 v2.2.7 + gopkg.in/yaml.v2 v2.2.8 ) go 1.14 diff --git a/go.sum b/go.sum index 7c4d8d6b..04e19b34 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/DataDog/zstd v1.4.0 h1:vhoV+DUHnRZdKW1i5UMjAk2G4JY8wN4ayRfYDNdEhwo= github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= -github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= +github.com/GeertJohan/go.rice v1.0.2 h1:PtRw+Tg3oa3HYwiDBZyvOJ8LdIyf6lAovJJtr7YOAYk= +github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Sereal/Sereal v0.0.0-20190430203904-6faf9605eb56 h1:3trCIB5GsAOIY8NxlfMztCYIhVsW9V5sZ+brsecjaiI= github.com/Sereal/Sereal v0.0.0-20190430203904-6faf9605eb56/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= @@ -33,6 +33,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/daaku/go.zipexe v1.0.1 h1:wV4zMsDOI2SZ2m7Tdz1Ps96Zrx+TzaK15VbUaGozw0M= @@ -70,6 +71,8 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -107,6 +110,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -143,8 +147,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow= +github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -172,7 +176,9 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -280,8 +286,8 @@ golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2V golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -318,8 +324,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/http/public.go b/http/public.go index d34b97e3..fd798180 100644 --- a/http/public.go +++ b/http/public.go @@ -1,6 +1,7 @@ package http import ( + "errors" "net/http" "path" "path/filepath" @@ -9,6 +10,7 @@ import ( "github.com/spf13/afero" "github.com/filebrowser/filebrowser/v2/files" + "github.com/filebrowser/filebrowser/v2/share" ) var withHashFile = func(fn handleFunc) handleFunc { @@ -19,6 +21,11 @@ var withHashFile = func(fn handleFunc) handleFunc { return errToStatus(err), err } + status, err := authenticateShareRequest(r, link, d.server.Salt) + if err != nil { + return status, err + } + user, err := d.store.Users.Get(d.server.Root, link.UserID) if err != nil { return errToStatus(err), err @@ -27,12 +34,13 @@ var withHashFile = func(fn handleFunc) handleFunc { d.user = user file, err := files.NewFileInfo(files.FileOptions{ - Fs: d.user.Fs, - Path: link.Path, - Modify: d.user.Perm.Modify, - Expand: true, - ReadHeader: d.server.TypeDetectionByHeader, - Checker: d, + Fs: d.user.Fs, + Path: link.Path, + Modify: d.user.Perm.Modify, + Expand: true, + ReadHeader: d.server.TypeDetectionByHeader, + Checker: d, + SharedCodeToken: link.SharedCodeToken, }) if err != nil { return errToStatus(err), err @@ -43,11 +51,12 @@ var withHashFile = func(fn handleFunc) handleFunc { d.user.Fs = afero.NewBasePathFs(d.user.Fs, filepath.Dir(link.Path)) file, err = files.NewFileInfo(files.FileOptions{ - Fs: d.user.Fs, - Path: path, - Modify: d.user.Perm.Modify, - Expand: true, - Checker: d, + Fs: d.user.Fs, + Path: path, + Modify: d.user.Perm.Modify, + Expand: true, + Checker: d, + SharedCodeToken: link.SharedCodeToken, }) if err != nil { return errToStatus(err), err @@ -94,3 +103,20 @@ var publicDlHandler = withHashFile(func(w http.ResponseWriter, r *http.Request, return rawDirHandler(w, r, d, file) }) + +func authenticateShareRequest(r *http.Request, l *share.Link, salt string) (int, error) { + if l.SharedCode == "" { + return 0, nil + } + + sharedCodeToken := r.URL.Query().Get("shared_code_token") + if sharedCodeToken == l.SharedCodeToken { + return 0, nil + } + + sharedCode := r.Header.Get("X-SHARED-CODE") + if l.SharedCode != sharedCode { + return http.StatusUnauthorized, errors.New("invalid shared code") + } + return 0, nil +} diff --git a/http/share.go b/http/share.go index 853a178e..6e706ef8 100644 --- a/http/share.go +++ b/http/share.go @@ -82,6 +82,10 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request rawExpire := r.URL.Query().Get("expires") unit := r.URL.Query().Get("unit") + if r.Body != nil { + defer r.Body.Close() + } + if rawExpire == "" { var err error s, err = d.store.Share.GetPermanent(r.URL.Path, d.user.ID) @@ -104,6 +108,7 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request var expire int64 = 0 if rawExpire != "" { + //nolint:govet num, err := strconv.Atoi(rawExpire) if err != nil { return http.StatusInternalServerError, err @@ -124,11 +129,30 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request expire = time.Now().Add(add).Unix() } - s = &share.Link{ - Path: r.URL.Path, - Hash: str, - Expire: expire, - UserID: d.user.ID, + sharedCode := r.URL.Query().Get("shared_code") + if sharedCode == "" { + s = &share.Link{ + Path: r.URL.Path, + Hash: str, + Expire: expire, + UserID: d.user.ID, + SharedCodeToken: "", + SharedCode: "", + } + } else { + tokenBuffer := make([]byte, 96) + if _, err := rand.Read(tokenBuffer); err != nil { + return http.StatusInternalServerError, err + } + token := base64.URLEncoding.EncodeToString(tokenBuffer) + s = &share.Link{ + Path: r.URL.Path, + Hash: str, + Expire: expire, + UserID: d.user.ID, + SharedCodeToken: token, + SharedCode: sharedCode, + } } if err := d.store.Share.Save(s); err != nil { diff --git a/settings/settings.go b/settings/settings.go index 0f8616d2..8a185952 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -38,6 +38,7 @@ type Server struct { Port string `json:"port"` Address string `json:"address"` Log string `json:"log"` + Salt string `json:"salt"` EnableThumbnails bool `json:"enableThumbnails"` ResizePreview bool `json:"resizePreview"` EnableExec bool `json:"enableExec"` diff --git a/share/share.go b/share/share.go index fb4329f1..b4b5a314 100644 --- a/share/share.go +++ b/share/share.go @@ -2,8 +2,10 @@ package share // Link is the information needed to build a shareable link. type Link struct { - Hash string `json:"hash" storm:"id,index"` - Path string `json:"path" storm:"index"` - UserID uint `json:"userID"` - Expire int64 `json:"expire"` + Hash string `json:"hash" storm:"id,index"` + Path string `json:"path" storm:"index"` + UserID uint `json:"userID"` + Expire int64 `json:"expire"` + SharedCode string `json:"shared_code,omitempty"` + SharedCodeToken string `json:"token,omitempty"` } diff --git a/share/storage.go b/share/storage.go index 4cd263de..904f7093 100644 --- a/share/storage.go +++ b/share/storage.go @@ -102,7 +102,9 @@ func (s *Storage) Gets(path string, id uint) ([]*Link, error) { if err := s.Delete(link.Hash); err != nil { return nil, err } - links = append(links[:i], links[i+1:]...) + if len(links) > i+1 { + links = append(links[:i], links[i+1:]...) + } } } diff --git a/wizard.sh b/wizard.sh index 2f1d87b8..b21c0cc5 100755 --- a/wizard.sh +++ b/wizard.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/bash set -e @@ -35,7 +35,8 @@ buildAssets () { buildBinary () { if ! [ -x "$(command -v rice)" ]; then - go install github.com/GeertJohan/go.rice/rice + go get github.com/GeertJohan/go.rice + go get github.com/GeertJohan/go.rice/rice fi cd $REPO/http @@ -55,28 +56,24 @@ release () { echo "❌ This release script requires a single argument corresponding to the semver to be released. See semver.org" exit 1 fi - +echo "1" GREP="grep" if [ -x "$(command -v ggrep)" ]; then GREP="ggrep" fi - - semver=$(echo "$1" | $GREP -P '^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)') - - if [ $? -ne 0 ]; then + semver=$(echo "$1" | $GREP -P "^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)") +err=$? + if [ $err -ne 0 ]; then echo "❌ Not valid semver format. See semver.org" exit 1 fi - echo "🧼 Tidying up go modules" go mod tidy - echo "🐑 Creating a new commit for the new release" git commit --allow-empty -am "chore: version $semver" git tag "$1" git push git push --tags origin - echo "📦 Done! $semver released." }