diff --git a/go.mod b/go.mod index 51b7381..19dd495 100644 --- a/go.mod +++ b/go.mod @@ -1,63 +1,69 @@ module github.com/cernbox/reva-plugins -go 1.21.0 +go 1.22.7 require ( github.com/Masterminds/sprig v2.22.0+incompatible github.com/bluele/gcache v0.0.2 - github.com/cs3org/go-cs3apis v0.0.0-20240802083356-d617314e1795 + github.com/cs3org/go-cs3apis v0.0.0-20250128143938-cc671934a7a1 github.com/cs3org/reva v1.27.0 github.com/disintegration/imaging v1.6.2 - github.com/go-chi/chi/v5 v5.0.12 - github.com/go-sql-driver/mysql v1.8.0 + github.com/go-chi/chi/v5 v5.2.0 + github.com/go-sql-driver/mysql v1.8.1 github.com/gomodule/redigo v1.9.2 github.com/juliangruber/go-intersect v1.1.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 - github.com/rs/zerolog v1.32.0 - google.golang.org/genproto v0.0.0-20240314234333-6e1732d8331c - google.golang.org/grpc v1.65.0 + github.com/rs/zerolog v1.33.0 + google.golang.org/genproto v0.0.0-20241209162323-e6fa225c2576 + google.golang.org/grpc v1.69.2 + gorm.io/datatypes v1.2.4 + gorm.io/driver/mysql v1.5.7 + gorm.io/driver/sqlite v1.5.7 + gorm.io/gorm v1.25.12 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect - github.com/cern-eos/go-eosgrpc v0.0.0-20240812132646-f105d2304f38 // indirect - github.com/creasty/defaults v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/cern-eos/go-eosgrpc v0.0.0-20240909164147-ad693be93181 // indirect + github.com/creasty/defaults v1.8.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect github.com/glpatcern/go-mime v0.0.0-20221026162842-2a8d71ad17a9 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect - github.com/go-ldap/ldap/v3 v3.4.6 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect + github.com/go-ldap/ldap/v3 v3.4.9 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.19.0 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/huandu/xstrings v1.4.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v0.3.16 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mileusna/useragent v1.3.4 // indirect + github.com/mattn/go-sqlite3 v1.14.24 // indirect + github.com/mileusna/useragent v1.3.5 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect - go.step.sm/crypto v0.43.1 // indirect - golang.org/x/crypto v0.23.0 // indirect + go.step.sm/crypto v0.55.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/image v0.13.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/protobuf v1.36.1 // indirect ) replace github.com/cs3org/reva => ../reva diff --git a/go.sum b/go.sum index 15195f5..3327963 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -12,71 +12,107 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM= github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/cern-eos/go-eosgrpc v0.0.0-20240812132646-f105d2304f38 h1:+81ss4Vut1khzEhl7ximWF/V+EadspY47V4JrQkwlI4= -github.com/cern-eos/go-eosgrpc v0.0.0-20240812132646-f105d2304f38/go.mod h1:ZiIzbg4sDO2MwYlspcnauUR2dfwZHUzxker+HP9k+20= +github.com/cern-eos/go-eosgrpc v0.0.0-20240909164147-ad693be93181 h1:2ZK/g0kTWuO98hM8uCuGnFwtljWhjI3k6nTQJLYj8vE= +github.com/cern-eos/go-eosgrpc v0.0.0-20240909164147-ad693be93181/go.mod h1:ZiIzbg4sDO2MwYlspcnauUR2dfwZHUzxker+HP9k+20= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= -github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= -github.com/cs3org/go-cs3apis v0.0.0-20240802083356-d617314e1795 h1:8WkweBxMQ1W6IhcK0X3eWY+aQCjEktGwVt/4KLrtOZ8= -github.com/cs3org/go-cs3apis v0.0.0-20240802083356-d617314e1795/go.mod h1:yyP8PRo0EZou3nSH7H4qjlzQwaydPeIRNgX50npQHpE= +github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= +github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= +github.com/cs3org/go-cs3apis v0.0.0-20250128143938-cc671934a7a1 h1:/WqyxkOGmocjx9ryVNGpwY3h+Fsm2VeQWweARkk0Svw= +github.com/cs3org/go-cs3apis v0.0.0-20250128143938-cc671934a7a1/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= +github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U= github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM= github.com/glpatcern/go-mime v0.0.0-20221026162842-2a8d71ad17a9 h1:3um08ooi0/lyRmK2eE1XTKmRQHDzPu0IvpCPMljyMZ8= github.com/glpatcern/go-mime v0.0.0-20221026162842-2a8d71ad17a9/go.mod h1:EJaddanP+JfU3UkVvn0rYYF3b/gD7eZRejbTHqiQExA= -github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= -github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= -github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= +github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo= +github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= -github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/juliangruber/go-intersect v1.1.0 h1:sc+y5dCjMMx0pAdYk/N6KBm00tD/f3tq+Iox7dYDUrY= github.com/juliangruber/go-intersect v1.1.0/go.mod h1:WMau+1kAmnlQnKiikekNJbtGtfmILU/mMU6H7AgKbWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -89,8 +125,12 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mileusna/useragent v1.3.4 h1:MiuRRuvGjEie1+yZHO88UBYg8YBC/ddF6T7F56i3PCk= -github.com/mileusna/useragent v1.3.4/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= +github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= +github.com/mileusna/useragent v1.3.5 h1:SJM5NzBmh/hO+4LGeATKpaEX9+b4vcGg2qXGLiNGDws= +github.com/mileusna/useragent v1.3.5/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -101,36 +141,50 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.step.sm/crypto v0.43.1 h1:18Z/M49SnFDPXvFbfoN/ugE1i0J7phLWARhSQs/XSDI= -go.step.sm/crypto v0.43.1/go.mod h1:9n90D/SWjH1hTyQn1hgviUGyK8YRv743S8UZHYbt4BU= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.step.sm/crypto v0.55.0 h1:575Q7NahuM/ZRxUVN1GkO2e1aDYQJqIIg+nbfOajQJk= +go.step.sm/crypto v0.55.0/go.mod h1:MgEmD1lgwsuzZwTgI0GwKapHjKVEQLVggSvHuf3bYnU= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= @@ -141,23 +195,35 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -170,21 +236,30 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -193,24 +268,39 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20240314234333-6e1732d8331c h1:1AVpelW1Ld8u6QbfPlwh00uAsR3xrnfn6FIJsCags3k= -google.golang.org/genproto v0.0.0-20240314234333-6e1732d8331c/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto v0.0.0-20241209162323-e6fa225c2576 h1:k48HcZ4FE6in0o8IflZCkc1lTc2u37nhGd8P+fo4r24= +google.golang.org/genproto v0.0.0-20241209162323-e6fa225c2576/go.mod h1:DV2u3tCn/AcVjjmGYZKt6HyvY4w4y3ipAdHkMbe/0i4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= +gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= +gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= +gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= diff --git a/share/model.go b/share/model.go new file mode 100644 index 0000000..1aa7267 --- /dev/null +++ b/share/model.go @@ -0,0 +1,180 @@ +package share + +import ( + "strconv" + "time" + + grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + resourcespb "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + conversions "github.com/cs3org/reva/pkg/cbox/utils" + + "gorm.io/datatypes" + "gorm.io/gorm" +) + +type ItemType string + +const ( + ItemTypeFile ItemType = "file" + ItemTypeFolder ItemType = "folder" + ItemTypeReference ItemType = "reference" + ItemTypeSymlink ItemType = "symlink" +) + +func (i ItemType) String() string { + return string(i) +} + +type ProtoShare struct { + // Including gorm.Model will embed a number of gorm-default fields + gorm.Model + UIDOwner string + UIDInitiator string + ItemType ItemType // file | folder | reference | symlink + InitialPath string + Inode string + Instance string + Permissions uint8 + Orphan bool + Expiration datatypes.NullTime +} + +type Share struct { + ProtoShare + ShareWith string + SharedWithIsGroup bool + Description string +} + +type PublicLink struct { + ProtoShare + Token string + // Enforce uniqueness in db re: Itemsource + Quicklink bool + NotifyUploads bool + NotifyUploadsExtraRecipients string + Password string + // Users can give a name to a share + LinkName string +} + +type ShareState struct { + gorm.Model + ShareID uint `gorm:"foreignKey:ShareID;references:ID"` // Define the foreign key field + Share Share // Define the association + // Can not be uid because of lw accs + User string + Synced bool + Hidden bool + Alias string +} + +func (s *Share) AsCS3Share(granteeType userpb.UserType) *collaboration.Share { + creationTs := &typespb.Timestamp{ + Seconds: uint64(s.CreatedAt.Unix()), + } + updateTs := &typespb.Timestamp{ + Seconds: uint64(s.UpdatedAt.Unix()), + } + return &collaboration.Share{ + Id: &collaboration.ShareId{ + OpaqueId: strconv.FormatUint(uint64(s.ID), 10), + }, + //ResourceId: &provider.Reference{StorageId: s.Prefix, NodeId: s.ItemSource}, + ResourceId: &provider.ResourceId{ + StorageId: s.Instance, + OpaqueId: s.Inode, + }, + Permissions: &collaboration.SharePermissions{Permissions: conversions.IntTosharePerm(int(s.Permissions), s.ItemType.String())}, + Grantee: extractGrantee(s.SharedWithIsGroup, s.ShareWith, granteeType), + Owner: conversions.MakeUserID(s.UIDOwner), + Creator: conversions.MakeUserID(s.UIDInitiator), + Ctime: creationTs, + Mtime: updateTs, + Description: s.Description, + } +} + +func (s *Share) AsCS3ReceivedShare(state *ShareState, granteeType userpb.UserType) *collaboration.ReceivedShare { + // Currently, some implementations still rely on the ShareState to determine whether a file is hidden + // instead of using the field + var rsharestate resourcespb.ShareState + if state.Hidden { + rsharestate = resourcespb.ShareState_SHARE_STATE_REJECTED + } else { + rsharestate = resourcespb.ShareState_SHARE_STATE_ACCEPTED + } + + return &collaboration.ReceivedShare{ + Share: s.AsCS3Share(granteeType), + State: rsharestate, + Hidden: state.Hidden, + Alias: state.Alias, + } +} + +func (p *PublicLink) AsCS3PublicShare() *link.PublicShare { + ts := &typespb.Timestamp{ + Seconds: uint64(p.CreatedAt.Unix()), + } + pwd := false + if p.Password != "" { + pwd = true + } + var expires *typespb.Timestamp + if p.Expiration.Valid { + exp, err := p.Expiration.Value() + if err == nil { + expiration := exp.(time.Time) + expires = &typespb.Timestamp{ + Seconds: uint64(expiration.Unix()), + } + } + + } + return &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: strconv.Itoa(int(p.ID)), + }, + ResourceId: &provider.ResourceId{ + StorageId: p.Instance, + OpaqueId: p.Inode, + }, + Permissions: &link.PublicSharePermissions{Permissions: conversions.IntTosharePerm(int(p.Permissions), p.ItemType.String())}, + Owner: conversions.MakeUserID(p.UIDOwner), + Creator: conversions.MakeUserID(p.UIDInitiator), + Token: p.Token, + DisplayName: p.LinkName, + PasswordProtected: pwd, + Expiration: expires, + Ctime: ts, + Mtime: ts, + Quicklink: p.Quicklink, + NotifyUploads: p.NotifyUploads, + NotifyUploadsExtraRecipients: p.NotifyUploadsExtraRecipients, + } +} + +// ExtractGrantee retrieves the CS3API Grantee from a grantee type and username/groupname. +// The grantee userType is relevant only for users. +func extractGrantee(sharedWithIsGroup bool, g string, gtype userpb.UserType) *provider.Grantee { + var grantee provider.Grantee + if sharedWithIsGroup { + grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP + grantee.Id = &provider.Grantee_GroupId{GroupId: &grouppb.GroupId{ + OpaqueId: g, + }} + } else { + grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER + grantee.Id = &provider.Grantee_UserId{UserId: &userpb.UserId{ + OpaqueId: g, + Type: gtype, + }} + } + return &grantee +} diff --git a/share/sql/sql.go b/share/sql/sql.go index 285b84a..7e60fd6 100644 --- a/share/sql/sql.go +++ b/share/sql/sql.go @@ -1,4 +1,4 @@ -// Copyright 2018-2023 CERN +// Copyright 2018-2024 CERN // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,29 +20,30 @@ package sql import ( "context" - "database/sql" "fmt" - "path" "strconv" "strings" - "time" + model "github.com/cernbox/reva-plugins/share" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva" "github.com/cs3org/reva/pkg/appctx" conversions "github.com/cs3org/reva/pkg/cbox/utils" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" - "github.com/cs3org/reva/pkg/share" + revashare "github.com/cs3org/reva/pkg/share" "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + // Provides mysql drivers. _ "github.com/go-sql-driver/mysql" "github.com/pkg/errors" @@ -50,9 +51,6 @@ import ( ) const ( - shareTypeUser = 0 - shareTypeGroup = 1 - projectInstancesPrefix = "newproject" projectSpaceGroupsPrefix = "cernbox-project-" projectSpaceAdminGroupsSuffix = "-admins" @@ -71,6 +69,7 @@ func (mgr) RevaPlugin() reva.PluginInfo { } type config struct { + Engine string `mapstructure:"engine"` // mysql | sqlite DBUsername string `mapstructure:"db_username"` DBPassword string `mapstructure:"db_password"` DBHost string `mapstructure:"db_host"` @@ -81,7 +80,7 @@ type config struct { type mgr struct { c *config - db *sql.DB + db *gorm.DB } func (c *config) ApplyDefaults() { @@ -89,13 +88,31 @@ func (c *config) ApplyDefaults() { } // New returns a new share manager. -func New(ctx context.Context, m map[string]interface{}) (share.Manager, error) { +func New(ctx context.Context, m map[string]interface{}) (revashare.Manager, error) { var c config if err := cfg.Decode(m, &c); err != nil { return nil, err } - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName)) + var db *gorm.DB + var err error + switch c.Engine { + case "sqlite": + db, err = gorm.Open(sqlite.Open(c.DBName), &gorm.Config{}) + case "mysql": + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + default: // default is mysql + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true", c.DBUsername, c.DBPassword, c.DBHost, c.DBPort, c.DBName) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + } + if err != nil { + return nil, err + } + + // Migrate schemas + err = db.AutoMigrate(&model.Share{}, &model.PublicLink{}, &model.ShareState{}) + if err != nil { return nil, err } @@ -123,222 +140,164 @@ func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collabora Grantee: g.Grantee, } _, err := m.getByKey(ctx, key, true) - // share already exists + // TODO stricter error checking if err == nil { - return nil, errtypes.AlreadyExists(key.String()) + return nil, errors.New(errtypes.AlreadyExists(key.String()).Error()) } - now := time.Now().Unix() - ts := &typespb.Timestamp{ - Seconds: uint64(now), + var shareWith string + if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER { + shareWith = conversions.FormatUserID(g.Grantee.GetUserId()) + } else if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP { + // ShareWith is a group + shareWith = g.Grantee.GetGroupId().OpaqueId + } else { + return nil, errors.New("Unsuppored grantee type passed to Share()") } - shareType, shareWith := conversions.FormatGrantee(g.Grantee) - itemType := conversions.ResourceTypeToItem(md.Type) - targetPath := path.Join("/", path.Base(md.Path)) - permissions := conversions.SharePermToInt(g.Permissions.Permissions) - prefix := md.Id.StorageId - itemSource := md.Id.OpaqueId - fileSource, err := strconv.ParseUint(itemSource, 10, 64) - if err != nil { - // it can be the case that the item source may be a character string - // we leave fileSource blank in that case - fileSource = 0 + share := &model.Share{ + ShareWith: shareWith, + SharedWithIsGroup: g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP, } + share.UIDOwner = conversions.FormatUserID(md.Owner) + share.UIDInitiator = conversions.FormatUserID(user.Id) + share.InitialPath = md.Path + share.ItemType = model.ItemType(conversions.ResourceTypeToItem(md.Type)) + share.Inode = md.Id.OpaqueId + share.Instance = md.Id.StorageId + share.Permissions = uint8(conversions.SharePermToInt(g.Permissions.Permissions)) + share.Orphan = false - stmtString := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,share_with=?,file_target=?" - stmtValues := []interface{}{shareType, conversions.FormatUserID(md.Owner), conversions.FormatUserID(user.Id), itemType, prefix, itemSource, fileSource, permissions, now, shareWith, targetPath} - - stmt, err := m.db.Prepare(stmtString) - if err != nil { - return nil, err - } - result, err := stmt.Exec(stmtValues...) - if err != nil { - return nil, err - } - lastID, err := result.LastInsertId() - if err != nil { - return nil, err + res := m.db.Save(&share) + if res.Error != nil { + return nil, res.Error } - return &collaboration.Share{ - Id: &collaboration.ShareId{ - OpaqueId: strconv.FormatInt(lastID, 10), - }, - ResourceId: md.Id, - Permissions: g.Permissions, - Grantee: g.Grantee, - Owner: md.Owner, - Creator: user.Id, - Ctime: ts, - Mtime: ts, - }, nil + granteeType, _ := m.getUserType(ctx, share.ShareWith) + return share.AsCS3Share(granteeType), nil } -func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId, checkOwner bool) (*collaboration.Share, error) { - uid := conversions.FormatUserID(appctx.ContextMustGetUser(ctx).Id) - s := conversions.DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id=?" - params := []interface{}{id.OpaqueId} - if checkOwner { - query += " AND (uid_owner=? or uid_initiator=?)" - params = append(params, uid, uid) - } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } - return nil, err +// Get Share by ID. Does not return orphans. +func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*model.Share, error) { + var share model.Share + res := m.db.First(&share, id.OpaqueId) + + if res.RowsAffected == 0 || share.Orphan { + return nil, errtypes.NotFound(id.OpaqueId) } - // the grantee type is resolved afterwards when needed - return conversions.ConvertToCS3Share(s, userpb.UserType_USER_TYPE_INVALID), nil + + return &share, nil } -func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey, checkOwner bool) (*collaboration.Share, error) { +// Get Share by Key. Does not return orphans. +func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey, checkOwner bool) (*model.Share, error) { owner := conversions.FormatUserID(key.Owner) + + var share model.Share + _, shareWith := conversions.FormatGrantee(key.Grantee) + + query := m.db.Model(&share). + Where("orphan = ?", false). + Where("uid_owner = ?", owner). + Where("instance = ?", key.ResourceId.StorageId). + Where("inode = ?", key.ResourceId.OpaqueId). + Where("shared_with_is_group = ?", key.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP). + Where("share_with = ?", strings.ToLower(shareWith)) + uid := conversions.FormatUserID(appctx.ContextMustGetUser(ctx).Id) + // In case the user is not the owner (i.e. in the case of projects) + if checkOwner && owner != uid { + query = query.Where("uid_initiator = ?", uid) + } - s := conversions.DBShare{} - shareType, shareWith := conversions.FormatGrantee(key.Grantee) - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, id, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND lower(share_with)=lower(?)" - params := []interface{}{owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith} - if checkOwner { - query += " AND (uid_owner=? or uid_initiator=?)" - params = append(params, uid, uid) - } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } - return nil, err + res := query.First(&share) + + if res.RowsAffected == 0 { + return nil, errtypes.NotFound(key.String()) } - // the grantee type is resolved afterwards when needed - return conversions.ConvertToCS3Share(s, userpb.UserType_USER_TYPE_INVALID), nil + + return &share, nil } -func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { - var s *collaboration.Share +func (m *mgr) getShare(ctx context.Context, ref *collaboration.ShareReference) (*model.Share, error) { + var s *model.Share var err error switch { case ref.GetId() != nil: - s, err = m.getByID(ctx, ref.GetId(), false) - if err != nil { - return nil, err - } + s, err = m.getByID(ctx, ref.GetId()) case ref.GetKey() != nil: s, err = m.getByKey(ctx, ref.GetKey(), false) - if err != nil { - return nil, err - } default: err = errtypes.NotFound(ref.String()) } - - // resolve grantee's user type if applicable - if s.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER { - s.Grantee.GetUserId().Type, _ = m.getUserType(ctx, s.Grantee.GetUserId().OpaqueId) - } - - path, err := m.getPath(ctx, s.ResourceId) if err != nil { return nil, err } user := appctx.ContextMustGetUser(ctx) - if m.isProjectAdmin(user, path) { + if s.UIDOwner == user.Id.OpaqueId && s.UIDInitiator == user.Id.OpaqueId { return s, nil } - if s.Owner.OpaqueId == user.Id.OpaqueId && s.Creator.OpaqueId == user.Id.OpaqueId { + path, err := m.getPath(ctx, &provider.ResourceId{ + StorageId: s.Instance, + OpaqueId: s.Inode, + }) + if err != nil { + return nil, err + } + + if m.isProjectAdmin(user, path) { return s, nil } - return s, errtypes.NotFound("share not found") + return nil, errtypes.NotFound(ref.String()) } -func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "delete from oc_share where id=?" - params = append(params, ref.GetId().OpaqueId) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith := conversions.FormatGrantee(key.Grantee) - owner := conversions.FormatUserID(key.Owner) - query = "delete from oc_share where uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND lower(share_with)=lower(?)" - params = append(params, owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith) - default: - return errtypes.NotFound(ref.String()) - } - - ctx, err := m.addPathIntoCtx(ctx, ref) +func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { + share, err := m.getShare(ctx, ref) if err != nil { - return err + return nil, err } - query, params, err = m.appendUidOwnerFilters(ctx, query, params) - if err != nil { - return err - } + granteeType, _ := m.getUserType(ctx, share.ShareWith) + cs3share := share.AsCS3Share(granteeType) - stmt, err := m.db.Prepare(query) - if err != nil { - return err - } - res, err := stmt.Exec(params...) - if err != nil { - return err - } + return cs3share, nil +} - rowCnt, err := res.RowsAffected() +func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { + var share *model.Share + var err error + if id := ref.GetId(); id != nil { + share, err = emptyShareWithId(id.OpaqueId) + } else { + share, err = m.getShare(ctx, ref) + } if err != nil { return err } - if rowCnt == 0 { - return errtypes.NotFound(ref.String()) - } - return nil + res := m.db.Delete(&share) + return res.Error } func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) { - permissions := conversions.SharePermToInt(p.Permissions) - - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "update oc_share set permissions=?,stime=? where id=?" - params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith := conversions.FormatGrantee(key.Grantee) - owner := conversions.FormatUserID(key.Owner) - query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND lower(share_with)=lower(?)" - params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith) - default: - return nil, errtypes.NotFound(ref.String()) - } - - ctx, err := m.addPathIntoCtx(ctx, ref) - if err != nil { - return nil, err + var share *model.Share + var err error + if id := ref.GetId(); id != nil { + share, err = emptyShareWithId(id.OpaqueId) + } else { + share, err = m.getShare(ctx, ref) } - - query, params, err = m.appendUidOwnerFilters(ctx, query, params) if err != nil { return nil, err } - stmt, err := m.db.Prepare(query) - if err != nil { - return nil, err - } - if _, err = stmt.Exec(params...); err != nil { - return nil, err + permissions := conversions.SharePermToInt(p.Permissions) + res := m.db.Model(&share).Update("permissions", uint8(permissions)) + if res.Error != nil { + return nil, res.Error } return m.GetShare(ctx, ref) @@ -358,40 +317,12 @@ func (m *mgr) getPath(ctx context.Context, resID *provider.ResourceId) (string, return "", err } - return res.GetPath(), nil -} - -func (m *mgr) addPathIntoCtx(ctx context.Context, ref *collaboration.ShareReference) (context.Context, error) { - var path string - var err error - switch { - case ref.GetId() != nil: - share, err := m.getByID(ctx, ref.GetId(), false) - if err != nil { - return nil, err - } - - path, err = m.getPath(ctx, share.ResourceId) - if err != nil { - return nil, err - } - case ref.GetKey() != nil: - key := ref.GetKey() - - path, err = m.getPath(ctx, key.ResourceId) - if err != nil { - return nil, err - } - } - return appctx.ContextSetResourcePath(ctx, path), nil -} - -func (m *mgr) isProjectAdminFromCtx(ctx context.Context, u *userpb.User) bool { - path, ok := appctx.ContextGetResourcePath(ctx) - if !ok { - return false + if res.Status.Code == rpc.Code_CODE_OK { + return res.GetPath(), nil + } else if res.Status.Code == rpc.Code_CODE_NOT_FOUND { + return "", errtypes.NotFound(resID.OpaqueId) } - return m.isProjectAdmin(u, path) + return "", errors.New(res.Status.Code.String() + ": " + res.Status.Message) } func (m *mgr) isProjectAdmin(u *userpb.User, path string) bool { @@ -407,14 +338,6 @@ func (m *mgr) isProjectAdmin(u *userpb.User, path string) bool { if g == adminGroup { // User belongs to the admin group, list all shares for the resource - // TODO: this only works if shares for a single project are requested. - // If shares for multiple projects are requested, then we're not checking if the - // user is an admin for all of those. We can append the query ` or uid_owner=?` - // for all the project owners, which works fine for new reva - // but won't work for revaold since there, we store the uid of the share creator as uid_owner. - // For this to work across the two versions, this change would have to be made in revaold - // but it won't be straightforward as there, the storage provider doesn't return the - // resource owners. return true } } @@ -423,169 +346,146 @@ func (m *mgr) isProjectAdmin(u *userpb.User, path string) bool { } func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { - query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, - coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, - id, stime, permissions, share_type - FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (share_type=? OR share_type=?)` - params := []interface{}{shareTypeUser, shareTypeGroup} - - groupedFilters := share.GroupFiltersByType(filters) - if len(groupedFilters) > 0 { - filterQuery, filterParams, err := translateFilters(groupedFilters) - if err != nil { - return nil, err - } - params = append(params, filterParams...) - if filterQuery != "" { - query = fmt.Sprintf("%s AND (%s)", query, filterQuery) - } - } + uid := conversions.FormatUserID(appctx.ContextMustGetUser(ctx).Id) - query, params, err := m.appendUidOwnerFilters(ctx, query, params) - if err != nil { - return nil, err - } + query := m.db.Model(&model.Share{}). + Where("uid_owner = ? or uid_initiator = ?", uid, uid). + Where("orphan = ?", false) - rows, err := m.db.Query(query, params...) - if err != nil { - return nil, err - } - defer rows.Close() + // Append filters + m.appendFiltersToQuery(query, filters) - var s conversions.DBShare - shares := []*collaboration.Share{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - continue - } - gtype, _ := m.getUserType(ctx, s.ShareWith) - // if err != nil { - // failed to resolve grantee's user type, TODO Log - // } - shares = append(shares, conversions.ConvertToCS3Share(s, gtype)) + var shares []model.Share + var cs3shares []*collaboration.Share + res := query.Find(&shares) + if res.Error != nil { + return nil, res.Error } - if err = rows.Err(); err != nil { - return nil, err + + for _, s := range shares { + granteeType, _ := m.getUserType(ctx, s.ShareWith) + cs3share := s.AsCS3Share(granteeType) + cs3shares = append(cs3shares, cs3share) } - return shares, nil + return cs3shares, nil } // we list the shares that are targeted to the user in context or to the user groups. func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.ReceivedShare, error) { user := appctx.ContextMustGetUser(ctx) - uid := conversions.FormatUserID(user.Id) - params := []interface{}{uid, uid, uid, uid} - for _, v := range user.Groups { - params = append(params, v) + // We need to do this to parse the result + // Normally, GORM would be able to fill in the Share that is referenced in ShareState + // However, in GORM's docs: "Join Preload will loads association data using left join" + // Because we do a RIGHT JOIN, GORM cannot load the data into shareState.Share (in case that ShareState is empty) + // So we load them both separately, and then set ShareState.Share = Share ourselves + var results []struct { + model.ShareState + model.Share } - query := `SELECT coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, - coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, - ts.id, stime, permissions, share_type, coalesce(tr.state, 0) as state - FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) - WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner != ? AND uid_initiator != ?)` - if len(user.Groups) > 0 { - query += " AND ((lower(share_with)=lower(?) AND share_type = 0) OR (share_type = 1 AND lower(share_with) in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" - } else { - query += " AND (lower(share_with)=lower(?) AND share_type = 0)" - } + query := m.db.Model(&model.ShareState{}). + Select("share_states.*, shares.*"). + Joins("RIGHT OUTER JOIN shares ON shares.id = share_states.share_id"). + Where("shares.orphan = ?", false) - groupedFilters := share.GroupFiltersByType(filters) - filterQuery, filterParams, err := translateFilters(groupedFilters) - if err != nil { - return nil, err + // Also search by all the groups the user is a member of + innerQuery := m.db.Where("shares.share_with = ? and shares.shared_with_is_group = ?", user.Username, false) + for _, group := range user.Groups { + innerQuery = innerQuery.Or("shares.share_with = ? and shares.shared_with_is_group = ?", group, true) } - params = append(params, filterParams...) + query = query.Where(innerQuery) - if filterQuery != "" { - query = fmt.Sprintf("%s AND (%s)", query, filterQuery) + // Append filters + m.appendFiltersToQuery(query, filters) + + // Get the shares + states + res := query.Find(&results) + if res.Error != nil { + return nil, res.Error } - rows, err := m.db.Query(query, params...) - if err != nil { - return nil, err + var receivedShares []*collaboration.ReceivedShare + + // Now we parse everything into the CS3 definition of a CS3ReceivedShare + for _, res := range results { + shareState := res.ShareState + shareState.Share = res.Share + granteeType, _ := m.getUserType(ctx, res.Share.ShareWith) + + receivedShares = append(receivedShares, res.Share.AsCS3ReceivedShare(&shareState, granteeType)) } - defer rows.Close() - var s conversions.DBShare - shares := []*collaboration.ReceivedShare{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - continue + return receivedShares, nil +} + +func (m *mgr) getShareState(ctx context.Context, share *model.Share, user *userpb.User) (*model.ShareState, error) { + var shareState model.ShareState + query := m.db.Model(&shareState). + Where("share_id = ?", share.ID). + Where("user = ?", user.Username) + + res := query.First(&shareState) + + if res.RowsAffected == 0 { + // If no share state has been created yet, we create it now using these defaults + shareState = model.ShareState{ + Share: *share, + Hidden: false, + Synced: false, + User: user.Username, } - gtype, _ := m.getUserType(ctx, s.ShareWith) - // if err != nil { - // failed to resolve grantee's user type, TODO Log - // } - shares = append(shares, conversions.ConvertToCS3ReceivedShare(s, gtype)) } - if err = rows.Err(); err != nil { + + return &shareState, nil +} + +func emptyShareWithId(id string) (*model.Share, error) { + intId, err := strconv.Atoi(id) + if err != nil { return nil, err } - - return shares, nil + share := &model.Share{ + ProtoShare: model.ProtoShare{ + Model: gorm.Model{ + ID: uint(intId), + }, + }, + } + return share, nil } func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId, gtype userpb.UserType) (*collaboration.ReceivedShare, error) { user := appctx.ContextMustGetUser(ctx) - uid := conversions.FormatUserID(user.Id) - - params := []interface{}{uid, id.OpaqueId, uid} - for _, v := range user.Groups { - params = append(params, v) + share, err := m.getByID(ctx, id) + if err != nil { + return nil, err } - s := conversions.DBShare{ID: id.OpaqueId} - query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, - coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, - stime, permissions, share_type, coalesce(tr.state, 0) as state - FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) - WHERE (orphan = 0 or orphan IS NULL) AND ts.id=?` - if len(user.Groups) > 0 { - query += " AND ((lower(share_with)=lower(?) AND share_type = 0) OR (share_type = 1 AND lower(share_with) in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" - } else { - query += " AND (lower(share_with)=lower(?) AND share_type = 0)" - } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } + shareState, err := m.getShareState(ctx, share, user) + if err != nil { return nil, err } - return conversions.ConvertToCS3ReceivedShare(s, gtype), nil + + receivedShare := share.AsCS3ReceivedShare(shareState, gtype) + return receivedShare, nil } func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey, gtype userpb.UserType) (*collaboration.ReceivedShare, error) { user := appctx.ContextMustGetUser(ctx) - uid := conversions.FormatUserID(user.Id) - - shareType, shareWith := conversions.FormatGrantee(key.Grantee) - params := []interface{}{uid, conversions.FormatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith} - for _, v := range user.Groups { - params = append(params, v) - } - - s := conversions.DBShare{} - query := `select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, lower(coalesce(share_with, '')) as share_with, - coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(item_type, '') as item_type, - ts.id, stime, permissions, share_type, coalesce(tr.state, 0) as state - FROM oc_share ts LEFT JOIN oc_share_status tr ON (ts.id = tr.id AND tr.recipient = ?) - WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND lower(share_with)=lower(?)` - if len(user.Groups) > 0 { - query += " AND ((lower(share_with)=lower(?) AND share_type = 0) OR (share_type = 1 AND lower(share_with) in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))" - } else { - query += " AND (lower(share_with)=lower(?) AND share_type = 0)" + share, err := m.getByKey(ctx, key, false) + if err != nil { + return nil, err } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ItemType, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } + shareState, err := m.getShareState(ctx, share, user) + if err != nil { return nil, err } - return conversions.ConvertToCS3ReceivedShare(s, gtype), nil + + receivedShare := share.AsCS3ReceivedShare(shareState, gtype) + return receivedShare, nil } func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { @@ -612,132 +512,51 @@ func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareRefe return s, nil } -func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { +func (m *mgr) UpdateReceivedShare(ctx context.Context, recvShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { + user := appctx.ContextMustGetUser(ctx) - rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Share.Id}}) + rs, err := m.getReceivedByID(ctx, recvShare.Share.Id, user.Id.Type) if err != nil { return nil, err } - for i := range fieldMask.Paths { - switch fieldMask.Paths[i] { - case "state": - rs.State = share.State - default: - return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") - } - } - - state := 0 - switch rs.GetState() { - case collaboration.ShareState_SHARE_STATE_REJECTED: - state = -1 - case collaboration.ShareState_SHARE_STATE_ACCEPTED: - state = 1 - } - - params := []interface{}{rs.Share.Id.OpaqueId, conversions.FormatUserID(user.Id), state, state} - query := "insert into oc_share_status(id, recipient, state) values(?, ?, ?) ON DUPLICATE KEY UPDATE state = ?" - - stmt, err := m.db.Prepare(query) + share, err := emptyShareWithId(recvShare.Share.Id.OpaqueId) if err != nil { return nil, err } - _, err = stmt.Exec(params...) - if err != nil { - return nil, err - } - - return rs, nil -} -func (m *mgr) appendUidOwnerFilters(ctx context.Context, query string, params []interface{}) (string, []interface{}, error) { - uidOwnersQuery, uidOwnersParams, err := m.uidOwnerFilters(ctx) + shareState, err := m.getShareState(ctx, share, user) if err != nil { - return "", nil, err - } - - params = append(params, uidOwnersParams...) - if uidOwnersQuery != "" { - query = fmt.Sprintf("%s AND (%s)", query, uidOwnersQuery) + return nil, err } - return query, params, nil -} - -func (m *mgr) uidOwnerFilters(ctx context.Context) (string, []interface{}, error) { - user := appctx.ContextMustGetUser(ctx) - uid := conversions.FormatUserID(user.Id) - - query := "uid_owner=? or uid_initiator=?" - params := []interface{}{uid, uid} - - if m.isProjectAdminFromCtx(ctx, user) { - return "", []interface{}{}, nil + // FieldMask determines which parts of the share we actually update + for _, path := range fieldMask.Paths { + switch path { + case "state": + rs.State = recvShare.State + switch rs.State { + case collaboration.ShareState_SHARE_STATE_ACCEPTED: + shareState.Hidden = false + case collaboration.ShareState_SHARE_STATE_REJECTED: + shareState.Hidden = true + } + case "hidden": + rs.Hidden = recvShare.Hidden + default: + return nil, errtypes.NotSupported("updating " + path + " is not supported") + } } - return query, params, nil -} + // Now we do the actual update to the db model -func granteeTypeToShareType(granteeType provider.GranteeType) int { - switch granteeType { - case provider.GranteeType_GRANTEE_TYPE_USER: - return shareTypeUser - case provider.GranteeType_GRANTEE_TYPE_GROUP: - return shareTypeGroup + res := m.db.Save(&shareState) + if res.Error != nil { + return nil, res.Error } - return -1 -} - -// translateFilters translates the filters to sql queries. -func translateFilters(filters map[collaboration.Filter_Type][]*collaboration.Filter) (string, []interface{}, error) { - var ( - filterQuery string - params []interface{} - ) - - // If multiple filters of the same type are passed to this function, they need to be combined with the `OR` operator. - // That is why the filters got grouped by type. - // For every given filter type, iterate over the filters and if there are more than one combine them. - // Combine the different filter types using `AND` - var filterCounter = 0 - for filterType, currFilters := range filters { - switch filterType { - case collaboration.Filter_TYPE_RESOURCE_ID: - filterQuery += "(" - for i, f := range currFilters { - filterQuery += "(fileid_prefix =? AND item_source=?)" - params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId) - if i != len(currFilters)-1 { - filterQuery += " OR " - } - } - filterQuery += ")" - case collaboration.Filter_TYPE_GRANTEE_TYPE: - filterQuery += "(" - for i, f := range currFilters { - filterQuery += "share_type=?" - params = append(params, granteeTypeToShareType(f.GetGranteeType())) - - if i != len(currFilters)-1 { - filterQuery += " OR " - } - } - filterQuery += ")" - case collaboration.Filter_TYPE_EXCLUDE_DENIALS: - // TODO this may change once the mapping of permission to share types is completed (cf. pkg/cbox/utils/conversions.go) - filterQuery += "(permissions > 0)" - default: - return "", nil, fmt.Errorf("filter type is not supported") - } - if filterCounter != len(filters)-1 { - filterQuery += " AND " - } - filterCounter++ - } - return filterQuery, params, nil + return rs, nil } func (m *mgr) getUserType(ctx context.Context, username string) (userpb.UserType, error) { @@ -758,3 +577,40 @@ func (m *mgr) getUserType(ctx context.Context, username string) (userpb.UserType return userRes.GetUser().Id.Type, nil } + +func (m *mgr) appendFiltersToQuery(query *gorm.DB, filters []*collaboration.Filter) { + // We want to chain filters of different types with AND + // and filters of the same type with OR + // Therefore, we group them by type + groupedFilters := revashare.GroupFiltersByType(filters) + + for filtertype, filters := range groupedFilters { + switch filtertype { + case collaboration.Filter_TYPE_RESOURCE_ID: + innerQuery := m.db + for i, filter := range filters { + if i == 0 { + innerQuery = innerQuery.Where("instance = ? and inode = ?", filter.GetResourceId().StorageId, filter.GetResourceId().OpaqueId) + } else { + innerQuery = innerQuery.Or("instance = ? and inode = ?", filter.GetResourceId().StorageId, filter.GetResourceId().OpaqueId) + } + } + query = query.Where(innerQuery) + case collaboration.Filter_TYPE_EXCLUDE_DENIALS: + query = query.Where("permissions > ?", 0) + case collaboration.Filter_TYPE_GRANTEE_TYPE: + innerQuery := m.db + for i, filter := range filters { + isGroup := filter.GetGranteeType() == provider.GranteeType_GRANTEE_TYPE_GROUP + if i == 0 { + innerQuery = innerQuery.Where("shared_with_is_group = ?", isGroup) + } else { + innerQuery = innerQuery.Or("shared_with_is_group = ? ", isGroup) + } + } + query = query.Where(innerQuery) + default: + break + } + } +} diff --git a/share/sql/sql_test.go b/share/sql/sql_test.go new file mode 100644 index 0000000..dbe738a --- /dev/null +++ b/share/sql/sql_test.go @@ -0,0 +1,670 @@ +package sql + +import ( + "context" + "log" + "os" + "testing" + "time" + + groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/pkg/appctx" + conversions "github.com/cs3org/reva/pkg/cbox/utils" + revashare "github.com/cs3org/reva/pkg/share" + "google.golang.org/genproto/protobuf/field_mask" +) + +// =========================== +// Helper functions for tests +// =========================== + +// You can use testing.T, if you want to test the code without benchmarking +func setupSuite(tb testing.TB) (revashare.Manager, error, func(tb testing.TB)) { + ctx := context.Background() + dbName := "test_db.sqlite" + cfg := map[string]interface{}{ + "engine": "sqlite", + "db_name": dbName, + } + mgr, err := New(ctx, cfg) + if err != nil { + return nil, err, nil + } + + // Return a function to teardown the test + return mgr, nil, func(tb testing.TB) { + log.Println("teardown suite") + os.Remove(dbName) + } +} + +func getRandomFile(owner *userpb.User) *provider.ResourceInfo { + return &provider.ResourceInfo{ + Id: &provider.ResourceId{ + StorageId: "project-b", + OpaqueId: "45468401564", + }, + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Path: "/eos/project/b/myfile", + Owner: owner.Id, + Mtime: &typesv1beta1.Timestamp{Seconds: uint64(time.Now().Unix())}, + } +} + +// Return context populated with user info +func getUserContext(id string) context.Context { + user := &userpb.User{ + Id: &userpb.UserId{ + OpaqueId: id, + Type: userpb.UserType_USER_TYPE_APPLICATION, + }, + Username: id, + Mail: "myuser@cern.ch", + MailVerified: true, + } + ctx := appctx.ContextSetUser(context.Background(), user) + return ctx +} + +func getUserShareGrant(shareeId, resourcetype string) *collaboration.ShareGrant { + sharee := &provider.Grantee{ + Type: provider.GranteeType_GRANTEE_TYPE_USER, + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Type: userpb.UserType_USER_TYPE_APPLICATION, + OpaqueId: shareeId, + }, + }, + } + + sharegrant := &collaboration.ShareGrant{ + Grantee: sharee, + Permissions: &collaboration.SharePermissions{ + Permissions: conversions.IntTosharePerm(1, resourcetype), + }, + } + return sharegrant +} + +func getGroupShareGrant(shareeId, resourcetype string) *collaboration.ShareGrant { + sharee := &provider.Grantee{ + Type: provider.GranteeType_GRANTEE_TYPE_GROUP, + Id: &provider.Grantee_GroupId{ + GroupId: &groupv1beta1.GroupId{ + OpaqueId: shareeId, + }, + }, + } + return &collaboration.ShareGrant{ + Grantee: sharee, + Permissions: &collaboration.SharePermissions{ + Permissions: conversions.IntTosharePerm(1, resourcetype), + }, + } +} + +// =========================== +// Actual tests +// =========================== + +func TestGetShareById(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + retrievedshare, err := mgr.GetShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + if retrievedshare.Id.OpaqueId != share.Id.OpaqueId { + t.Errorf("Retrieved share does not match created share, expected %s, got %s", share.Id.OpaqueId, retrievedshare.Id.OpaqueId) + } +} + +func TestGetShareByKey(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + key := &collaboration.ShareKey{ + Owner: user.Id, + ResourceId: file.Id, + Grantee: sharegrant.Grantee, + } + + retrievedshare, err := mgr.GetShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Key{ + Key: key, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + if retrievedshare.Id.OpaqueId != share.Id.OpaqueId { + t.Errorf("Retrieved share does not match created share, expected %s, got %s", share.Id.OpaqueId, retrievedshare.Id.OpaqueId) + } +} + +func TestGetReceivedShareById(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + receivedshare, err := mgr.GetReceivedShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + if receivedshare.Share.Id.OpaqueId != share.Id.OpaqueId { + t.Errorf("Retrieved received share does not match created share, expected %s, got %s", share.Id.OpaqueId, receivedshare.Share.Id.OpaqueId) + } +} + +func TestGetReceivedShareByKey(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + receivingUserCtx := getUserContext("1000") + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + key := &collaboration.ShareKey{ + Owner: user.Id, + ResourceId: file.Id, + Grantee: sharegrant.Grantee, + } + + receivedshare, err := mgr.GetReceivedShare(receivingUserCtx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Key{ + Key: key, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + if receivedshare.Share.Id.OpaqueId != share.Id.OpaqueId { + t.Errorf("Retrieved received share does not match created share, expected %s, got %s", share.Id.OpaqueId, receivedshare.Share.Id.OpaqueId) + } +} + +func TestDoNotCreateSameShareTwice(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + sharee := &provider.Grantee{ + Type: provider.GranteeType_GRANTEE_TYPE_USER, + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Type: userpb.UserType_USER_TYPE_APPLICATION, + OpaqueId: "1000", + }, + }, + } + sharegrant := &collaboration.ShareGrant{ + Grantee: sharee, + Permissions: &collaboration.SharePermissions{ + Permissions: conversions.IntTosharePerm(1, "file"), + }, + } + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + + _, err = mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + _, err = mgr.Share(userctx, file, sharegrant) + if err == nil { + t.Log("Creating same share succeeded, while it should have failed") + t.FailNow() + } +} + +func TestListShares(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + res, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + shares, err := mgr.ListShares(userctx, nil) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(shares) != 1 { + t.Errorf("Expected 1 share, got %d", len(shares)) + t.FailNow() + } + + if shares[0].Id.OpaqueId != res.Id.OpaqueId { + t.Errorf("Expected share with id %s, got %s", res.Id.OpaqueId, shares[0].Id.OpaqueId) + } +} + +func TestListReceivedShares(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + receivingUserCtx := getUserContext("1000") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + res, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + receivedShares, err := mgr.ListReceivedShares(receivingUserCtx, nil) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(receivedShares) != 1 { + t.Errorf("Expected 1 received share, got %d", len(receivedShares)) + t.FailNow() + } + + if receivedShares[0].Share.Id.OpaqueId != res.Id.OpaqueId { + t.Errorf("Expected share with id %s, got %s", res.Id.OpaqueId, receivedShares[0].Share.Id.OpaqueId) + } +} + +func TestListSharesWithFilters(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + res, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + filters := []*collaboration.Filter{ + { + Type: collaboration.Filter_TYPE_RESOURCE_ID, + Term: &collaboration.Filter_ResourceId{ + ResourceId: &provider.ResourceId{ + StorageId: "project-b", + OpaqueId: "45468401564", + }, + }, + }, + } + + shares, err := mgr.ListShares(userctx, filters) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(shares) != 1 { + t.Errorf("Expected 1 share, got %d", len(shares)) + } + + if shares[0].Id.OpaqueId != res.Id.OpaqueId { + t.Errorf("Expected share with id %s, got %s", res.Id.OpaqueId, shares[0].Id.OpaqueId) + } +} + +func TestDeleteShare(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + err = mgr.Unshare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + _, err = mgr.GetShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err == nil { + t.Errorf("Expected share to be deleted, but it was not") + } +} + +func TestUpdateShare(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + newPermissions := 15 + + updatedShare, err := mgr.UpdateShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }, &collaboration.SharePermissions{ + Permissions: conversions.IntTosharePerm(newPermissions, "file"), + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + retrievedPerms := conversions.SharePermToInt(updatedShare.Permissions.Permissions) + if retrievedPerms != newPermissions { + t.Errorf("Expected share permissions to be updated, but they were not: got %d instead of %d", retrievedPerms, newPermissions) + } +} + +func TestUpdateReceivedShare(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + share, err := mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + receivedShare, err := mgr.GetReceivedShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + // Update the received share state + receivedShare.State = collaboration.ShareState_SHARE_STATE_REJECTED + + _, err = mgr.UpdateReceivedShare(userctx, receivedShare, &field_mask.FieldMask{ + Paths: []string{"state"}, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + updatedReceivedShare, err := mgr.GetReceivedShare(userctx, &collaboration.ShareReference{ + Spec: &collaboration.ShareReference_Id{ + Id: share.Id, + }, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + if updatedReceivedShare.State != collaboration.ShareState_SHARE_STATE_REJECTED { + t.Errorf("Expected received share state to be updated, but it was not") + } +} + +func TestShareWithGroup(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getGroupShareGrant("myegroup", "file") + + _, err = mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + shares, err := mgr.ListShares(userctx, nil) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(shares) != 1 { + t.Errorf("Expected 1 share, got %d", len(shares)) + } + + if shares[0].Grantee.Type != provider.GranteeType_GRANTEE_TYPE_GROUP { + t.Errorf("Expected share to be with a group, but it was not") + } +} + +func TestListSharesWithGranteeTypeFilter(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getGroupShareGrant("myegroup", "file") + + _, err = mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + filters := []*collaboration.Filter{ + { + Type: collaboration.Filter_TYPE_GRANTEE_TYPE, + Term: &collaboration.Filter_GranteeType{ + GranteeType: provider.GranteeType_GRANTEE_TYPE_GROUP, + }, + }, + } + + shares, err := mgr.ListShares(userctx, filters) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(shares) != 1 { + t.Errorf("Expected 1 share, got %d", len(shares)) + } +} + +func TestListSharesWithMultipleFilters(t *testing.T) { + mgr, err, teardown := setupSuite(t) + defer teardown(t) + + if err != nil { + t.Error(err) + } + + userctx := getUserContext("123456") + user, _ := appctx.ContextGetUser(userctx) + file := getRandomFile(user) + sharegrant := getUserShareGrant("1000", "file") + + _, err = mgr.Share(userctx, file, sharegrant) + if err != nil { + t.Error(err) + t.FailNow() + } + + filters := []*collaboration.Filter{ + { + Type: collaboration.Filter_TYPE_RESOURCE_ID, + Term: &collaboration.Filter_ResourceId{ResourceId: &provider.ResourceId{ + StorageId: "project-b", + OpaqueId: "45468401564", + }}, + }, + { + Type: collaboration.Filter_TYPE_GRANTEE_TYPE, + Term: &collaboration.Filter_GranteeType{ + GranteeType: provider.GranteeType_GRANTEE_TYPE_USER, + }, + }, + } + + shares, err := mgr.ListShares(userctx, filters) + if err != nil { + t.Error(err) + t.FailNow() + } + + if len(shares) != 1 { + t.Errorf("Expected 1 share, got %d", len(shares)) + } +}