diff --git a/go.mod b/go.mod index 0e9e3170..e5337650 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.14.0 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 - github.com/aws/aws-sdk-go v1.42.52 + github.com/aws/aws-sdk-go v1.42.53 github.com/cockroachdb/cockroach-go/v2 v2.2.8 github.com/coreos/go-oidc/v3 v3.1.0 github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b @@ -53,7 +53,7 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/automaxprocs v1.4.0 gocloud.dev v0.24.0 - golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed + golang.org/x/crypto v0.0.0-20220214200702-86341886e292 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sys v0.0.0-20220209214540-3681064d5158 @@ -64,8 +64,8 @@ require ( require ( cloud.google.com/go v0.100.2 // indirect - cloud.google.com/go/compute v1.2.0 // indirect - cloud.google.com/go/iam v0.1.1 // indirect + cloud.google.com/go/compute v1.3.0 // indirect + cloud.google.com/go/iam v0.2.0 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect @@ -139,6 +139,6 @@ require ( replace ( github.com/eikenb/pipeat => github.com/drakkan/pipeat v0.0.0-20210805162858-70e57fa8a639 github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 - golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220130095207-a206cf284b7c + golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220215181150-74469fa99b22 golang.org/x/net => github.com/drakkan/net v0.0.0-20220130095023-bd85f1236c34 ) diff --git a/go.sum b/go.sum index fbf56f2c..c36557de 100644 --- a/go.sum +++ b/go.sum @@ -46,14 +46,16 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.2.0 h1:EKki8sSdvDU0OO9mAXGwPXOTOgPz2l08R0/IutDH11I= cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= +cloud.google.com/go/compute v1.3.0 h1:mPL/MzDDYHsh5tHRS9mhmhWlcgClCrCa6ApQCU6wnHI= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= +cloud.google.com/go/iam v0.2.0 h1:Ouq6qif4mZdXkb3SiFMpxvu0JQJB1Yid9TsZ23N6hg8= +cloud.google.com/go/iam v0.2.0/go.mod h1:BCK88+tmjAwnZYfOSizmKCTSFjJHCa18t3DpdGEY13Y= cloud.google.com/go/kms v0.1.0 h1:VXAb5OzejDcyhFzIDeZ5n5AUdlsFnCyexuascIwWMj0= cloud.google.com/go/kms v0.1.0/go.mod h1:8Qp8PCAypHg4FdmlyW1QRAv09BGQ9Uzh7JnmIZxPk+c= cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= @@ -141,8 +143,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.52 h1:/+TZ46+0qu9Ph/UwjVrU3SG8OBi87uJLrLiYRNZKbHQ= -github.com/aws/aws-sdk-go v1.42.52/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.53 h1:56T04NWcmc0ZVYFbUc6HdewDQ9iHQFlmS6hj96dRjJs= +github.com/aws/aws-sdk-go v1.42.53/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= @@ -216,8 +218,8 @@ github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mz github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/drakkan/crypto v0.0.0-20220130095207-a206cf284b7c h1:IqTZK/MGRdMPRyyJQSxDtrEokSJDJl+nreM2/CFYTsg= -github.com/drakkan/crypto v0.0.0-20220130095207-a206cf284b7c/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro= +github.com/drakkan/crypto v0.0.0-20220215181150-74469fa99b22 h1:yHFyJbCfvTY65bTyPOMRGqplA5GNfdZhBVOiYGNeCtY= +github.com/drakkan/crypto v0.0.0-20220215181150-74469fa99b22/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= github.com/drakkan/net v0.0.0-20220130095023-bd85f1236c34 h1:DRayAKtBRaVU3jg58b/HCbkRleByBD5q6NkN1wcJ2RU= @@ -1082,6 +1084,7 @@ google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFd google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.68.0 h1:9eJiHhwJKIYX6sX2fUZxQLi7pDRA/MYu8c12q6WbJik= google.golang.org/api v0.68.0/go.mod h1:sOM8pTpwgflXRhz+oC8H2Dr+UcbMqkPPWNJo88Q7TH8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -1171,6 +1174,7 @@ google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220211171837-173942840c17 h1:2X+CNIheCutWRyKRte8szGxrE5ggtV4U+NKAbh/oLhg= google.golang.org/genproto v0.0.0-20220211171837-173942840c17/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= diff --git a/httpd/api_shares.go b/httpd/api_shares.go index 4403b269..7d17b1b6 100644 --- a/httpd/api_shares.go +++ b/httpd/api_shares.go @@ -138,7 +138,7 @@ func deleteShare(w http.ResponseWriter, r *http.Request) { func readBrowsableShareContents(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead, false) if err != nil { return } @@ -165,7 +165,7 @@ func readBrowsableShareContents(w http.ResponseWriter, r *http.Request) { func downloadBrowsableSharedFile(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead, false) if err != nil { return } @@ -211,7 +211,7 @@ func downloadBrowsableSharedFile(w http.ResponseWriter, r *http.Request) { func downloadFromShare(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead, false) if err != nil { return } @@ -263,7 +263,7 @@ func uploadFileToShare(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxUploadFileSize) } name := getURLParam(r, "name") - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeWrite) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeWrite, false) if err != nil { return } @@ -285,7 +285,7 @@ func uploadFilesToShare(w http.ResponseWriter, r *http.Request) { if maxUploadFileSize > 0 { r.Body = http.MaxBytesReader(w, r.Body, maxUploadFileSize) } - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeWrite) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeWrite, false) if err != nil { return } @@ -332,40 +332,53 @@ func uploadFilesToShare(w http.ResponseWriter, r *http.Request) { } func checkPublicShare(w http.ResponseWriter, r *http.Request, shareShope dataprovider.ShareScope, + isWebClient bool, ) (dataprovider.Share, *Connection, error) { + renderError := func(err error, message string, statusCode int) { + if isWebClient { + renderClientMessagePage(w, r, "Unable to access the share", message, statusCode, err, "") + } else { + sendAPIResponse(w, r, err, message, statusCode) + } + } + shareID := getURLParam(r, "id") share, err := dataprovider.ShareExists(shareID, "") if err != nil { - sendAPIResponse(w, r, err, "", getRespStatus(err)) + statusCode := getRespStatus(err) + if statusCode == http.StatusNotFound { + err = errors.New("share does not exist") + } + renderError(err, "", statusCode) return share, nil, err } if share.Scope != shareShope { - sendAPIResponse(w, r, nil, "Invalid share scope", http.StatusForbidden) + renderError(nil, "Invalid share scope", http.StatusForbidden) return share, nil, errors.New("invalid share scope") } ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr) ok, err := share.IsUsable(ipAddr) if !ok || err != nil { - sendAPIResponse(w, r, err, "", getRespStatus(err)) - return share, nil, errors.New("login not allowed") + renderError(err, "", getRespStatus(err)) + return share, nil, err } if share.Password != "" { _, password, ok := r.BasicAuth() if !ok { w.Header().Set(common.HTTPAuthenticationHeader, basicRealm) - sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + renderError(dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return share, nil, dataprovider.ErrInvalidCredentials } match, err := share.CheckPassword(password) if !match || err != nil { w.Header().Set(common.HTTPAuthenticationHeader, basicRealm) - sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + renderError(dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return share, nil, dataprovider.ErrInvalidCredentials } } user, err := dataprovider.UserExists(share.Username) if err != nil { - sendAPIResponse(w, r, err, "", getRespStatus(err)) + renderError(err, "", getRespStatus(err)) return share, nil, err } connID := xid.New().String() diff --git a/httpd/httpd_test.go b/httpd/httpd_test.go index 3afec763..2790d87e 100644 --- a/httpd/httpd_test.go +++ b/httpd/httpd_test.go @@ -9412,6 +9412,12 @@ func TestShareUploadSingle(t *testing.T) { if assert.NoError(t, err) { assert.InDelta(t, util.GetTimeAsMsSinceEpoch(modTime), util.GetTimeAsMsSinceEpoch(info.ModTime()), float64(1000)) } + req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, objectID, "upload"), nil) + assert.NoError(t, err) + req.SetBasicAuth(defaultUsername, defaultPassword) + rr = executeRequest(req) + checkResponseCode(t, http.StatusOK, rr) + req, err = http.NewRequest(http.MethodPost, path.Join(webClientPubSharesPath, objectID, "file.txt"), bytes.NewBuffer(content)) assert.NoError(t, err) req.SetBasicAuth(defaultUsername, defaultPassword) @@ -9668,6 +9674,12 @@ func TestBrowseShares(t *testing.T) { objectID := rr.Header().Get("X-Object-ID") assert.NotEmpty(t, objectID) + req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, objectID, "upload"), nil) + assert.NoError(t, err) + rr = executeRequest(req) + checkResponseCode(t, http.StatusForbidden, rr) + assert.Contains(t, rr.Body.String(), "Invalid share scope") + req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, objectID, "browse?path=%2F"), nil) assert.NoError(t, err) rr = executeRequest(req) diff --git a/httpd/server.go b/httpd/server.go index 60fa60a8..e03876c4 100644 --- a/httpd/server.go +++ b/httpd/server.go @@ -1306,8 +1306,9 @@ func (s *httpdServer) setupWebClientRoutes() { Post(webClientTwoFactorRecoveryPath, s.handleWebClientTwoFactorRecoveryPost) // share API exposed to external users s.router.Get(webClientPubSharesPath+"/{id}", downloadFromShare) - s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles) - s.router.With(compressor.Handler).Get(webClientPubSharesPath+"/{id}/dirs", s.handleShareGetDirContents) + s.router.Get(webClientPubSharesPath+"/{id}/browse", handleShareGetFiles) + s.router.Get(webClientPubSharesPath+"/{id}/upload", handleClientUploadToShare) + s.router.With(compressor.Handler).Get(webClientPubSharesPath+"/{id}/dirs", handleShareGetDirContents) s.router.Post(webClientPubSharesPath+"/{id}", uploadFilesToShare) s.router.Post(webClientPubSharesPath+"/{id}/{name}", uploadFileToShare) diff --git a/httpd/webclient.go b/httpd/webclient.go index 1697e9ac..eda47600 100644 --- a/httpd/webclient.go +++ b/httpd/webclient.go @@ -46,6 +46,7 @@ const ( templateClientShares = "shares.html" templateClientViewPDF = "viewpdf.html" templateShareFiles = "sharefiles.html" + templateUploadToShare = "shareupload.html" pageClientFilesTitle = "My Files" pageClientSharesTitle = "Shares" pageClientProfileTitle = "My Profile" @@ -55,6 +56,7 @@ const ( pageClientForgotPwdTitle = "SFTPGo WebClient - Forgot password" pageClientResetPwdTitle = "SFTPGo WebClient - Reset password" pageExtShareTitle = "Shared files" + pageUploadToShareTitle = "Upload to share" ) // condResult is the result of an HTTP request precondition check. @@ -147,6 +149,12 @@ type shareFilesPage struct { Paths []dirMapping } +type shareUploadPage struct { + baseClientPage + Share *dataprovider.Share + UploadBasePath string +} + type clientMessagePage struct { baseClientPage Error string @@ -261,6 +269,10 @@ func loadClientTemplates(templatesPath string) { filepath.Join(templatesPath, templateClientDir, templateClientBase), filepath.Join(templatesPath, templateClientDir, templateShareFiles), } + shareUploadPath := []string{ + filepath.Join(templatesPath, templateClientDir, templateClientBase), + filepath.Join(templatesPath, templateClientDir, templateUploadToShare), + } filesTmpl := util.LoadTemplate(nil, filesPaths...) profileTmpl := util.LoadTemplate(nil, profilePaths...) @@ -277,6 +289,7 @@ func loadClientTemplates(templatesPath string) { resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...) viewPDFTmpl := util.LoadTemplate(nil, viewPDFPaths...) shareFilesTmpl := util.LoadTemplate(nil, shareFilesPath...) + shareUploadTmpl := util.LoadTemplate(nil, shareUploadPath...) clientTemplates[templateClientFiles] = filesTmpl clientTemplates[templateClientProfile] = profileTmpl @@ -293,6 +306,7 @@ func loadClientTemplates(templatesPath string) { clientTemplates[templateResetPassword] = resetPwdTmpl clientTemplates[templateClientViewPDF] = viewPDFTmpl clientTemplates[templateShareFiles] = shareFilesTmpl + clientTemplates[templateUploadToShare] = shareUploadTmpl } func getBaseClientPageData(title, currentURL string, r *http.Request) baseClientPage { @@ -495,6 +509,16 @@ func renderSharedFilesPage(w http.ResponseWriter, r *http.Request, dirName, erro renderClientTemplate(w, templateShareFiles, data) } +func renderUploadToSharePage(w http.ResponseWriter, r *http.Request, share dataprovider.Share) { + currentURL := path.Join(webClientPubSharesPath, share.ShareID, "upload") + data := shareUploadPage{ + baseClientPage: getBaseClientPageData(pageUploadToShareTitle, currentURL, r), + Share: &share, + UploadBasePath: path.Join(webClientPubSharesPath, share.ShareID), + } + renderClientTemplate(w, templateUploadToShare, data) +} + func renderFilesPage(w http.ResponseWriter, r *http.Request, dirName, error string, user dataprovider.User, hasIntegrations bool, ) { @@ -590,9 +614,9 @@ func handleWebClientDownloadZip(w http.ResponseWriter, r *http.Request) { renderCompressedFiles(w, connection, name, filesList, nil) } -func (s *httpdServer) handleShareGetDirContents(w http.ResponseWriter, r *http.Request) { +func handleShareGetDirContents(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead, true) if err != nil { return } @@ -636,9 +660,18 @@ func (s *httpdServer) handleShareGetDirContents(w http.ResponseWriter, r *http.R render.JSON(w, r, results) } -func (s *httpdServer) handleShareGetFiles(w http.ResponseWriter, r *http.Request) { +func handleClientUploadToShare(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead) + share, _, err := checkPublicShare(w, r, dataprovider.ShareScopeWrite, true) + if err != nil { + return + } + renderUploadToSharePage(w, r, share) +} + +func handleShareGetFiles(w http.ResponseWriter, r *http.Request) { + r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) + share, connection, err := checkPublicShare(w, r, dataprovider.ShareScopeRead, true) if err != nil { return } diff --git a/templates/webclient/shares.html b/templates/webclient/shares.html index 4c46b519..7619c619 100644 --- a/templates/webclient/shares.html +++ b/templates/webclient/shares.html @@ -93,16 +93,24 @@