Compare commits

...
5 Commits
9 changed files with 699 additions and 366 deletions

No files matched your search

+7
View File
@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
## v1.5.3
**BugFixes**:
- fix probe canShare with the real file, not a fixed text/plain stand-in (#2664)
- When the logout button is pressed, the user is redirected to an invalid URL that does not honor baseURL / externalUrl (#2657)
- fix redirect to login when an authenticated request returns 401 (expired session)
## v1.5.2
**Notes**:
+16 -16
View File
@@ -18,7 +18,7 @@ require (
github.com/gtsteffaniak/go-cache v1.1.0
github.com/gtsteffaniak/go-logger v1.1.0
github.com/kovidgoyal/imaging v1.8.23
github.com/mattn/go-sqlite3 v1.14.48
github.com/mattn/go-sqlite3 v1.14.49
github.com/pquerna/otp v1.5.0
github.com/spf13/afero v1.15.0
github.com/stretchr/testify v1.11.1
@@ -33,7 +33,7 @@ require (
golang.org/x/time v0.15.0
golang.org/x/tools v0.48.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.54.0
modernc.org/sqlite v1.55.0
)
require (
@@ -106,7 +106,7 @@ require (
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.10.1 // indirect
github.com/ebitengine/purego v0.10.2 // indirect
github.com/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
@@ -114,7 +114,7 @@ require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
github.com/ghostiam/protogetter v0.3.20 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
github.com/go-critic/go-critic v0.14.3 // indirect
@@ -122,14 +122,14 @@ require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-openapi/jsonpointer v1.0.0 // indirect
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/spec v0.22.6 // indirect
github.com/go-openapi/swag/conv v0.27.0 // indirect
github.com/go-openapi/swag/jsonname v0.27.0 // indirect
github.com/go-openapi/swag/jsonutils v0.27.0 // indirect
github.com/go-openapi/swag/loading v0.27.0 // indirect
github.com/go-openapi/swag/stringutils v0.27.0 // indirect
github.com/go-openapi/swag/typeutils v0.27.0 // indirect
github.com/go-openapi/swag/yamlutils v0.27.0 // indirect
github.com/go-openapi/spec v0.22.9 // indirect
github.com/go-openapi/swag/conv v0.28.0 // indirect
github.com/go-openapi/swag/jsonutils v0.28.0 // indirect
github.com/go-openapi/swag/loading v0.28.0 // indirect
github.com/go-openapi/swag/pools v0.28.0 // indirect
github.com/go-openapi/swag/stringutils v0.28.0 // indirect
github.com/go-openapi/swag/typeutils v0.28.0 // indirect
github.com/go-openapi/swag/yamlutils v0.28.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
@@ -192,7 +192,7 @@ require (
github.com/ldez/structtags v0.6.1 // indirect
github.com/ldez/tagliatelle v0.7.2 // indirect
github.com/ldez/usetesting v0.5.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/leodido/go-urn v1.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
@@ -204,7 +204,7 @@ require (
github.com/maratori/testpackage v1.1.2 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.23 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgechev/revive v1.15.0 // indirect
@@ -284,7 +284,7 @@ require (
go.etcd.io/bbolt v1.5.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
golang.org/x/sync v0.22.0 // indirect
@@ -293,7 +293,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.7.0 // indirect
modernc.org/libc v1.74.2 // indirect
modernc.org/libc v1.74.4 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
mvdan.cc/gofumpt v0.9.2 // indirect
+34 -33
View File
@@ -256,8 +256,8 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE=
github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -281,8 +281,8 @@ github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh
github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
github.com/gen2brain/go-fitz v1.28.2 h1:845G85N5TUgnq5oDqyYrW0JvehAkeo35UkkK2dJtW1M=
github.com/gen2brain/go-fitz v1.28.2/go.mod h1:pY2hqAjp9Zy7qfPI2gwbJMHBFAdZpVXOLrRxD82l3Bs=
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
@@ -320,25 +320,25 @@ github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxg
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
github.com/go-openapi/spec v0.22.6 h1:Tyy1pLaNCM8GBCFLoGYLonjJi6zykqyLCjXLc19ZPic=
github.com/go-openapi/spec v0.22.6/go.mod h1:HZvTHat+iH0PALQRWhrqIHtU/PEqxqd89fu0MxGlMeM=
github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w=
github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag/conv v0.27.0 h1:EKOH4feXrvdo8DbSsXSAqRT8fz1epEnS5O2IfXUOzE8=
github.com/go-openapi/swag/conv v0.27.0/go.mod h1:pfiv0uKQTbaGApk8Zs/lZV3uSjmSpa2FO1y183YngN8=
github.com/go-openapi/swag/jsonname v0.27.0 h1:4QVB//CKOdE8IOiBg19JNY2wfDS48MhesIquYBy2rUE=
github.com/go-openapi/swag/jsonname v0.27.0/go.mod h1:I1YsyvvhBuZsFXSW6I7ODfdyq13p7hDil//1T9/pFFk=
github.com/go-openapi/swag/jsonutils v0.27.0 h1:VYtd9jEQYeU4j8q5vdn5KWotF4vKywhGdMBrALtAsfE=
github.com/go-openapi/swag/jsonutils v0.27.0/go.mod h1:U7pb8AGuwhok3RDicHeHwSG4L3PXSq6PAL98Aon632g=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0 h1:+d7C7Ur/SsGg/UZ9G0JEovnfRqtMNZCJQGKc2h/ojoE=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
github.com/go-openapi/swag/loading v0.27.0 h1:s8DA9aPEdFH6OluHUYUn3DnIuoTdyWs9RwffXBUfyeI=
github.com/go-openapi/swag/loading v0.27.0/go.mod h1:VOz+Jg6UGGywcmRvYsI4fvtp+bd7NfioseGEPleYdA4=
github.com/go-openapi/swag/stringutils v0.27.0 h1:Of7w/HljWsNZvuxsUAnw3n+hCOyI6HLJOxW2kQRAxio=
github.com/go-openapi/swag/stringutils v0.27.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.27.0 h1:aCf4MSGo8NLwZP8Q6t32DWLJSvl/WwNqgmEG+xJ6v2o=
github.com/go-openapi/swag/typeutils v0.27.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.27.0 h1:bQ6eAMil5X9tdcf7dMn4t15alzG6jddnrKPuKa/zxKM=
github.com/go-openapi/swag/yamlutils v0.27.0/go.mod h1:yRfIo7qqVkmJRQjX8exjA3AfcI8rH1KDNPsTparoCv4=
github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8=
github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k=
github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4=
github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU=
github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k=
github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU=
github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU=
github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY=
github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY=
github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
@@ -628,8 +628,8 @@ github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYU
github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI=
github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc=
github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
@@ -658,12 +658,12 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ=
github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w=
github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q=
@@ -941,8 +941,9 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -1302,8 +1303,8 @@ modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.74.2 h1:r94QPC+Mh3WN1lSCDxQxFdIxcI+zdQROo+1pTEZGqkk=
modernc.org/libc v1.74.2/go.mod h1:4H7h/MJ8wnjL8RAbp9v3OXgnk22X7MouHIhDbvP3gj4=
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k=
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -1312,8 +1313,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.54.0 h1:JCxR4qwkJvOaqAoYcgDoO25Nc+ROg6EJ2LfBVzdrgog=
modernc.org/sqlite v1.54.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
modernc.org/sqlite v1.55.0 h1:hIFh0MCH0rGinQ/4KYb5/UbCkRkb+UP+OkLCVWa5MTM=
modernc.org/sqlite v1.55.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+347 -302
View File
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -191,14 +191,13 @@ export default {
let sharePath;
if (isShare && state.shareInfo?.hash) {
const shareBase = `/public/share/${state.shareInfo.hash}`;
const shareBase = `${globalVars.baseURL}public/share/${state.shareInfo.hash}`;
const subPath = state.req?.path && state.req.path !== '/'
? url.removeLeadingSlash(state.req.path)
: '';
sharePath = subPath ? `${shareBase}/${url.encodedPath(subPath)}` : shareBase;
}
await auth.logout();
window.location.href = sharePath || '/login';
await auth.logout(sharePath);
},
beforeEnter(el) {
el.style.maxHeight = '0';
+39 -6
View File
@@ -14,6 +14,21 @@ export async function validateLogin(isPublicRoute = false) {
});
if (res.status !== 200) {
// A 401 from the non-public self check means our session cookie (JWT) is no
// longer valid — typically it expired. Clear it and redirect to login so the
// user can re-authenticate, instead of leaving the stale cookie in place
// (which otherwise leaves the app stuck on reload). Only do this for a real,
// non-public session: public routes legitimately 401 for anonymous share
// visitors, and we skip it when there is no session cookie to clear.
if (
res.status === 401 &&
!isPublicRoute &&
document.cookie
.split(";")
.some((c) => c.trim().startsWith("filebrowser_quantum_jwt="))
) {
sessionExpired();
}
throw new Error(`{"status":${res.status},"message":"${await res.text()}"}`);
}
const userInfo = await res.json();
@@ -61,7 +76,7 @@ export function generateRandomCode(length) {
return code;
}
export async function logout() {
export async function logout(redirectUrl) {
try {
const res = await fetch(getApiPath("auth/logout"), {
method: "POST",
@@ -69,17 +84,17 @@ export async function logout() {
});
if (res.ok) {
const data = await res.json();
let logoutUrl = data.logoutUrl;
let destination = data.logoutUrl || `${globalVars.baseURL}login`;
if (redirectUrl) {
destination = redirectUrl;
}
// Backend clears the cookie, but frontend does it as fail-safe cleanup
document.cookie = "filebrowser_quantum_jwt=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
void mutations.setCurrentUser(null);
// No need to clear state.jwt - cookie is the source of truth
if (!logoutUrl) {
logoutUrl = `${globalVars.baseURL}login`;
}
// Add a small delay to ensure cookie deletion completes before redirect
setTimeout(() => {
window.location.href = logoutUrl;
window.location.href = destination;
}, 100);
return; // Stop execution
} else {
@@ -91,6 +106,24 @@ export async function logout() {
}
}
// Handle an authenticated request that came back 401 because the session cookie
// (JWT) is no longer valid — typically it expired while the tab was idle. Unlike
// logout(), this does NOT call the server (which would itself 401 on an expired
// token and leave the stale cookie in place); it clears the cookie locally and
// redirects to the login page so the user can re-authenticate, instead of being
// stuck on a raw "token is expired" error.
export function sessionExpired() {
document.cookie =
"filebrowser_quantum_jwt=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
void mutations.setCurrentUser(null);
// Avoid a redirect loop if we're already on the login page.
if (window.location.pathname.endsWith("/login")) {
return;
}
const current = window.location.pathname + window.location.search;
window.location.href = `${globalVars.baseURL}login?redirect=${encodeURIComponent(current)}`;
}
// Helper function to retrieve the value of a specific cookie
//function getCookie(name) {
// return document.cookie
+78
View File
@@ -0,0 +1,78 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
// Hoisted mocks referenced by the vi.mock factories below.
const { storeMock } = vi.hoisted(() => ({
storeMock: {
state: { sessionId: 'sess', user: null },
getters: { isLoggedIn: vi.fn(() => true) },
mutations: { setCurrentUser: vi.fn(), setSession: vi.fn() },
},
}));
vi.mock('@/store', () => storeMock);
vi.mock('@/utils/constants', () => ({ globalVars: { baseURL: '/', recaptcha: false } }));
vi.mock('@/utils/url.js', () => ({
getApiPath: (p, params = {}) =>
`/api/${p}${params.id ? `?id=${params.id}` : ''}`,
}));
import { validateLogin } from './auth.js';
const COOKIE = 'filebrowser_quantum_jwt';
function respond(status, body = '') {
global.fetch = vi.fn().mockResolvedValue({
status,
text: async () => body,
json: async () => ({}),
});
}
describe('validateLogin session-expiry handling', () => {
beforeEach(() => {
storeMock.mutations.setCurrentUser.mockClear();
document.cookie = `${COOKIE}=stale; path=/`;
// Stub navigation via Vitest's global-stub API (jsdom doesn't implement
// real navigation). pathname+search feed the redirect; href captures it.
vi.stubGlobal('location', { pathname: '/files/', search: '?a=1', href: '' });
});
afterEach(() => {
vi.unstubAllGlobals();
});
it('clears the cookie + user and redirects (with encoded return path) on a non-public 401 with a session cookie', async () => {
respond(401, 'token is expired');
await expect(validateLogin(false)).rejects.toThrow();
expect(storeMock.mutations.setCurrentUser).toHaveBeenCalledWith(null);
expect(document.cookie.includes(`${COOKIE}=stale`)).toBe(false);
// Full redirect contract: login route + correctly encoded current path+query.
expect(window.location.href).toBe(
`/login?redirect=${encodeURIComponent('/files/?a=1')}`
);
});
it('does NOT log out or redirect on a PUBLIC 401 (anonymous share visitor)', async () => {
respond(401, 'unauthorized');
await expect(validateLogin(true)).rejects.toThrow();
expect(storeMock.mutations.setCurrentUser).not.toHaveBeenCalledWith(null);
expect(document.cookie.includes(`${COOKIE}=stale`)).toBe(true);
expect(window.location.href).toBe('');
});
it('does NOT log out or redirect on a non-public 401 when there is NO session cookie', async () => {
document.cookie = `${COOKIE}=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/`;
respond(401, 'unauthorized');
await expect(validateLogin(false)).rejects.toThrow();
expect(storeMock.mutations.setCurrentUser).not.toHaveBeenCalledWith(null);
expect(window.location.href).toBe('');
});
it('does NOT log out or redirect on a non-401 error (e.g. 500)', async () => {
respond(500, 'server error');
await expect(validateLogin(false)).rejects.toThrow();
expect(storeMock.mutations.setCurrentUser).not.toHaveBeenCalledWith(null);
expect(document.cookie.includes(`${COOKIE}=stale`)).toBe(true);
expect(window.location.href).toBe('');
});
});
+23 -6
View File
@@ -10,13 +10,18 @@ export function canNativeShare() {
);
}
function canShareFiles() {
/**
* Checks whether the OS/browser share sheet can accept the given File.
* navigator.canShare({ files }) validates the specific file being shared
* (name, MIME type, size) — many platforms only allow a safe subset
* (images, audio, video, plain text, PDF) and reject everything else.
*/
function canShareFile(file) {
if (typeof navigator.canShare !== "function") {
return false;
}
try {
const probe = new File(["x"], "share-probe.txt", { type: "text/plain" });
return navigator.canShare({ files: [probe] });
return navigator.canShare({ files: [file] });
} catch {
return false;
}
@@ -43,7 +48,14 @@ export async function nativeShareFile(item) {
const url = buildDownloadUrl(item);
try {
if (canShareFiles()) {
// Only attempt to share the file's actual bytes if navigator.canShare
// exists at all. We must check the real file (fetched below), not a
// synthetic stand-in: this previously probed with a fixed text/plain
// file, which always reported success even for file types (e.g.
// .xlsx, .docx, .zip) that the OS share sheet then silently rejected
// once navigator.share() was actually called, surfacing as an opaque
// "permission denied" error to the user (see #2659).
if (typeof navigator.canShare === "function") {
const response = await fetch(url, { credentials: "same-origin" });
if (!response.ok) {
const detail = response.statusText ? ` (${response.statusText})` : "";
@@ -53,10 +65,15 @@ export async function nativeShareFile(item) {
const blob = await response.blob();
const type = blob.type || item.type || "application/octet-stream";
const file = new File([blob], filename, { type });
await navigator.share({ files: [file], title: filename });
return;
if (canShareFile(file)) {
await navigator.share({ files: [file], title: filename });
return;
}
}
// Fall back to sharing a link when the platform can't share the file's
// actual bytes/type (or doesn't support file sharing at all).
await navigator.share({ url, title: filename });
} catch (e) {
if (e?.name === "AbortError") {
+153
View File
@@ -0,0 +1,153 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const showErrorMock = vi.fn();
vi.mock("@/api", () => ({
resourcesApi: {
getDownloadURL: vi.fn(() => "https://example.com/api/raw/report.xlsx"),
getDownloadURLPublic: vi.fn(() => "https://example.com/api/public/dl/report.xlsx"),
},
}));
vi.mock("@/notify", () => ({
notify: {
showError: (...args) => showErrorMock(...args),
},
}));
vi.mock("@/store", () => ({
getters: {
isShare: () => false,
},
state: {
req: { source: "default" },
},
}));
import { canNativeShare, nativeShareFile } from "./nativeShare";
function mockShare(implementation) {
Object.defineProperty(window, "isSecureContext", {
configurable: true,
value: true,
});
navigator.share = vi.fn(implementation ?? (() => Promise.resolve()));
}
function stubFetch(blobType) {
global.fetch = vi.fn(() =>
Promise.resolve({
ok: true,
statusText: "OK",
status: 200,
blob: () =>
Promise.resolve({
type: blobType,
}),
})
);
}
beforeEach(() => {
showErrorMock.mockClear();
delete navigator.canShare;
delete navigator.share;
global.fetch = undefined;
});
describe("canNativeShare", () => {
it("returns false when navigator.share is unavailable", () => {
expect(canNativeShare()).toBe(false);
});
it("returns true when navigator.share exists and context is secure", () => {
mockShare();
expect(canNativeShare()).toBe(true);
});
});
describe("nativeShareFile", () => {
it("shows an error when sharing isn't supported at all", async () => {
await nativeShareFile({ name: "report.xlsx", path: "/report.xlsx" });
expect(showErrorMock).toHaveBeenCalledWith("Sharing is not supported on this device");
});
it(
"falls back to sharing a link (not the file) when the platform rejects the file's real MIME type " +
"-- regression test for #2659, where a hardcoded text/plain probe file made canShareFiles() always " +
"return true, so navigator.share() was called with an .xlsx/.docx/.zip file the platform then " +
"silently rejected, surfacing as 'permission denied'",
async () => {
mockShare();
stubFetch("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// Platform can share files in general, but rejects this specific
// (non-safelisted) MIME type -- exactly what real browsers do for
// office/archive formats.
navigator.canShare = vi.fn(({ files }) => {
const file = files?.[0];
return file ? file.type === "text/plain" : false;
});
await nativeShareFile({ name: "report.xlsx", path: "/report.xlsx" });
expect(navigator.share).toHaveBeenCalledTimes(1);
const sharedPayload = navigator.share.mock.calls[0][0];
// Must NOT have attempted to share the actual xlsx file bytes.
expect(sharedPayload.files).toBeUndefined();
expect(sharedPayload.url).toBe("https://example.com/api/raw/report.xlsx");
expect(showErrorMock).not.toHaveBeenCalled();
}
);
it("shares the real file when the platform's canShare accepts its actual type", async () => {
mockShare();
stubFetch("application/pdf");
navigator.canShare = vi.fn(({ files }) => files?.[0]?.type === "application/pdf");
await nativeShareFile({ name: "doc.pdf", path: "/doc.pdf" });
expect(navigator.share).toHaveBeenCalledTimes(1);
const sharedPayload = navigator.share.mock.calls[0][0];
expect(sharedPayload.files).toHaveLength(1);
expect(sharedPayload.files[0].name).toBe("doc.pdf");
expect(sharedPayload.files[0].type).toBe("application/pdf");
});
it("shares a link directly when the platform has no canShare at all", async () => {
mockShare();
// No navigator.canShare defined -- older/limited platforms.
await nativeShareFile({ name: "report.xlsx", path: "/report.xlsx" });
expect(global.fetch).toBeUndefined();
expect(navigator.share).toHaveBeenCalledWith({
url: "https://example.com/api/raw/report.xlsx",
title: "report.xlsx",
});
});
it("surfaces a download failure instead of silently sharing nothing", async () => {
mockShare();
navigator.canShare = vi.fn(() => true);
global.fetch = vi.fn(() =>
Promise.resolve({ ok: false, status: 403, statusText: "Forbidden" })
);
await nativeShareFile({ name: "report.xlsx", path: "/report.xlsx" });
expect(navigator.share).not.toHaveBeenCalled();
expect(showErrorMock).toHaveBeenCalledWith("Download failed (403 (Forbidden))");
});
it("silently ignores the user cancelling the OS share sheet", async () => {
mockShare(() => {
const err = new Error("cancelled");
err.name = "AbortError";
return Promise.reject(err);
});
await nativeShareFile({ name: "report.xlsx", path: "/report.xlsx" });
expect(showErrorMock).not.toHaveBeenCalled();
});
});