diff --git a/CHANGELOG.md b/CHANGELOG.md index 4903d1244e..f9ff3c0ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,114 @@ +Changelog for reva 1.27.0 (2023-10-19) +======================================= + +The following sections list the changes in reva 1.27.0 relevant to +reva users. The changes are ordered by importance. + +Summary +------- + + * Fix #4196: Access public links to projects as owner + * Enh #4266: Improve authentication routing logic + * Enh #4212: CERNBox cleanup + * Enh #4199: Dynamic storage provider + * Enh #4264: Implement eos-compliant app locks + * Enh #4200: Multiple fixes for Ceph driver + * Enh #4185: Refurbish the grpc and https plugins for eos + * Enh #4166: Add better observability with metrics and traces + * Enh #4195: Support incoming OCM 1.0 shares + * Enh #4189: Support full URL endpoints in ocm-provider + * Enh #4186: Fixes in the reference configuration for ScienceMesh + * Enh #4191: Add metrics service to ScienceMesh example config + +Details +------- + + * Bugfix #4196: Access public links to projects as owner + + https://github.com/cs3org/reva/pull/4196 + + * Enhancement #4266: Improve authentication routing logic + + Provides a safer approach to route requests, both in HTTP and gRPC land when authentication is + needed. + + https://github.com/cs3org/reva/pull/4266 + + * Enhancement #4212: CERNBox cleanup + + Remove from the codebase all the cernbox specific code + + https://github.com/cs3org/reva/pull/4212 + + * Enhancement #4199: Dynamic storage provider + + Add a new storage provider that can globally route to other providers. This provider uses a + routing table in the database containing `path` - `mountid` pairs, and a mapping `mountid` - + `address` in the config. It also support rewriting paths for resolution (to enable more + complex cases). + + https://github.com/cs3org/reva/pull/4199 + + * Enhancement #4264: Implement eos-compliant app locks + + The eosfs package now uses the app locks provided by eos + + https://github.com/cs3org/reva/pull/4264 + + * Enhancement #4200: Multiple fixes for Ceph driver + + * Avoid usage/creation of user homes when they are disabled in the config * Simplify the regular + uploads (not chunked) * Avoid creation of shadow folders at the root if they are already there * + Clean up the chunked upload * Fix panic on shutdown + + https://github.com/cs3org/reva/pull/4200 + + * Enhancement #4185: Refurbish the grpc and https plugins for eos + + This enhancement refurbishes the grpc and https plugins for eos + + https://github.com/cs3org/reva/pull/4185 + + * Enhancement #4166: Add better observability with metrics and traces + + Adds prometheus collectors that can be registered dynamically and also refactors the http and + grpc clients and servers to propage trace info. + + https://github.com/cs3org/reva/pull/4166 + + * Enhancement #4195: Support incoming OCM 1.0 shares + + OCM 1.0 payloads are now supported as incoming shares, and converted to the OCM 1.1 format for + persistency and further processing. Outgoing shares are still only OCM 1.1. + + https://github.com/cs3org/reva/pull/4195 + + * Enhancement #4189: Support full URL endpoints in ocm-provider + + This patch enables a reva server to properly show any configured endpoint route in all relevant + properties exposed by /ocm-provider. This allows reverse proxy configurations of the form + https://server/route to be supported for the OCM discovery mechanism. + + https://github.com/cs3org/reva/pull/4189 + + * Enhancement #4186: Fixes in the reference configuration for ScienceMesh + + Following the successful onboarding of CESNET, this PR brings some improvements and fixes to + the reference configuration, as well as some adaptation to the itegration tests. + + https://github.com/cs3org/reva/pull/4186 + https://github.com/cs3org/reva/pull/4184 + https://github.com/cs3org/reva/pull/4183 + + * Enhancement #4191: Add metrics service to ScienceMesh example config + + Adds the metrics http service configuration to the example config file of a ScienceMesh site. + Having this service configured is a prerequisite for successfull Prometheus-based + ScienceMesh sites metrics scraping. + + https://github.com/cs3org/reva/pull/4191 + + Changelog for reva 1.26.0 (2023-09-08) ======================================= diff --git a/RELEASE_DATE b/RELEASE_DATE index 2f7748b511..d85023a69a 100644 --- a/RELEASE_DATE +++ b/RELEASE_DATE @@ -1 +1 @@ -2023-09-08 \ No newline at end of file +2023-10-19 \ No newline at end of file diff --git a/VERSION b/VERSION index bc584045a3..e43da41f67 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.26.0 \ No newline at end of file +1.27.0 \ No newline at end of file diff --git a/changelog/1.27.0_2023-10-19/auth-fixes.md b/changelog/1.27.0_2023-10-19/auth-fixes.md new file mode 100644 index 0000000000..8ab4594a37 --- /dev/null +++ b/changelog/1.27.0_2023-10-19/auth-fixes.md @@ -0,0 +1,6 @@ +Enhancement: improve authentication routing logic + +Provides a safer approach to route requests, both in HTTP and gRPC land +when authentication is needed. + +https://github.com/cs3org/reva/pull/4266 diff --git a/changelog/unreleased/cbox-cleanup.md b/changelog/1.27.0_2023-10-19/cbox-cleanup.md similarity index 100% rename from changelog/unreleased/cbox-cleanup.md rename to changelog/1.27.0_2023-10-19/cbox-cleanup.md diff --git a/changelog/unreleased/dynamic-storage-provider.md b/changelog/1.27.0_2023-10-19/dynamic-storage-provider.md similarity index 100% rename from changelog/unreleased/dynamic-storage-provider.md rename to changelog/1.27.0_2023-10-19/dynamic-storage-provider.md diff --git a/changelog/1.27.0_2023-10-19/eos-locks.md b/changelog/1.27.0_2023-10-19/eos-locks.md new file mode 100644 index 0000000000..cea1035407 --- /dev/null +++ b/changelog/1.27.0_2023-10-19/eos-locks.md @@ -0,0 +1,5 @@ +Enhancement: implement eos-compliant app locks + +The eosfs package now uses the app locks provided by eos + +https://github.com/cs3org/reva/pull/4264 diff --git a/changelog/unreleased/fix-ceph-driver.md b/changelog/1.27.0_2023-10-19/fix-ceph-driver.md similarity index 100% rename from changelog/unreleased/fix-ceph-driver.md rename to changelog/1.27.0_2023-10-19/fix-ceph-driver.md diff --git a/changelog/1.27.0_2023-10-19/grpc-http-refurbish.md b/changelog/1.27.0_2023-10-19/grpc-http-refurbish.md new file mode 100644 index 0000000000..ba3b19b3b1 --- /dev/null +++ b/changelog/1.27.0_2023-10-19/grpc-http-refurbish.md @@ -0,0 +1,5 @@ +Enhancement: Refurbish the grpc and https plugins for eos + +This enhancement refurbishes the grpc and https plugins for eos + +https://github.com/cs3org/reva/pull/4185 diff --git a/changelog/unreleased/observability.md b/changelog/1.27.0_2023-10-19/observability.md similarity index 100% rename from changelog/unreleased/observability.md rename to changelog/1.27.0_2023-10-19/observability.md diff --git a/changelog/unreleased/ocm-10.md b/changelog/1.27.0_2023-10-19/ocm-10.md similarity index 100% rename from changelog/unreleased/ocm-10.md rename to changelog/1.27.0_2023-10-19/ocm-10.md diff --git a/changelog/unreleased/ocm-provider-root.md b/changelog/1.27.0_2023-10-19/ocm-provider-root.md similarity index 100% rename from changelog/unreleased/ocm-provider-root.md rename to changelog/1.27.0_2023-10-19/ocm-provider-root.md diff --git a/changelog/1.27.0_2023-10-19/public-share-owner.md b/changelog/1.27.0_2023-10-19/public-share-owner.md new file mode 100644 index 0000000000..ab411bcb62 --- /dev/null +++ b/changelog/1.27.0_2023-10-19/public-share-owner.md @@ -0,0 +1,3 @@ +Bugfix: access public links to projects as owner + +https://github.com/cs3org/reva/pull/4196 diff --git a/changelog/unreleased/sm-config-docs.md b/changelog/1.27.0_2023-10-19/sm-config-docs.md similarity index 100% rename from changelog/unreleased/sm-config-docs.md rename to changelog/1.27.0_2023-10-19/sm-config-docs.md diff --git a/changelog/unreleased/sm-config-metrics.md b/changelog/1.27.0_2023-10-19/sm-config-metrics.md similarity index 100% rename from changelog/unreleased/sm-config-metrics.md rename to changelog/1.27.0_2023-10-19/sm-config-metrics.md diff --git a/changelog/NOTE.md b/changelog/NOTE.md index 964557e314..db042ed519 100644 --- a/changelog/NOTE.md +++ b/changelog/NOTE.md @@ -1,102 +1,111 @@ -Changelog for reva 1.26.0 (2023-09-08) +Changelog for reva 1.27.0 (2023-10-19) ======================================= -The following sections list the changes in reva 1.26.0 relevant to +The following sections list the changes in reva 1.27.0 relevant to reva users. The changes are ordered by importance. Summary ------- - * Fix #4165: Use default user tmp folder in config tests - * Fix #4113: Fix plugin's registration when reva is built with version 1.21 - * Fix #4171: Fix accessing an OCM-shared resource containing spaces - * Fix #4172: Hardcode access methods for outgoing OCM shares from OC/NC - * Fix #4125: Enable projects for lightweight accounts - * Enh #4121: Expire cached users and groups entries - * Enh #4162: Disable sharing on a storage provider - * Enh #4163: Disable trashbin on a storage provider - * Enh #4164: Disable versions on a storage provider - * Enh #4084: Implementation of an app provider for Overleaf - * Enh #4114: List all the registered plugins - * Enh #4115: All required features and fixes for the OC/NC ScienceMesh apps + * Fix #4196: Access public links to projects as owner + * Enh #4266: Improve authentication routing logic + * Enh #4212: CERNBox cleanup + * Enh #4199: Dynamic storage provider + * Enh #4264: Implement eos-compliant app locks + * Enh #4200: Multiple fixes for Ceph driver + * Enh #4185: Refurbish the grpc and https plugins for eos + * Enh #4166: Add better observability with metrics and traces + * Enh #4195: Support incoming OCM 1.0 shares + * Enh #4189: Support full URL endpoints in ocm-provider + * Enh #4186: Fixes in the reference configuration for ScienceMesh + * Enh #4191: Add metrics service to ScienceMesh example config Details ------- - * Bugfix #4165: Use default user tmp folder in config tests + * Bugfix #4196: Access public links to projects as owner - https://github.com/cs3org/reva/pull/4165 + https://github.com/cs3org/reva/pull/4196 - * Bugfix #4113: Fix plugin's registration when reva is built with version 1.21 + * Enhancement #4266: Improve authentication routing logic - With go 1.21 the logic for package initialization has changed, and the plugins were failing in - the registration. Now the registration of the plugins is deferred in the main. + Provides a safer approach to route requests, both in HTTP and gRPC land when authentication is + needed. - https://github.com/cs3org/reva/pull/4113 + https://github.com/cs3org/reva/pull/4266 - * Bugfix #4171: Fix accessing an OCM-shared resource containing spaces + * Enhancement #4212: CERNBox cleanup - Fixes the access of a resource OCM-shared containing spaces, that previously was failing with - a `NotFound` error. + Remove from the codebase all the cernbox specific code - https://github.com/cs3org/reva/pull/4171 + https://github.com/cs3org/reva/pull/4212 - * Bugfix #4172: Hardcode access methods for outgoing OCM shares from OC/NC + * Enhancement #4199: Dynamic storage provider - This is a workaround until sciencemesh/nc-sciencemesh#45 is properly implemented + Add a new storage provider that can globally route to other providers. This provider uses a + routing table in the database containing `path` - `mountid` pairs, and a mapping `mountid` - + `address` in the config. It also support rewriting paths for resolution (to enable more + complex cases). - https://github.com/cs3org/reva/pull/4172 + https://github.com/cs3org/reva/pull/4199 - * Bugfix #4125: Enable projects for lightweight accounts + * Enhancement #4264: Implement eos-compliant app locks - Enable CERNBox projects to be listed by a lightweight account + The eosfs package now uses the app locks provided by eos - https://github.com/cs3org/reva/pull/4125 + https://github.com/cs3org/reva/pull/4264 - * Enhancement #4121: Expire cached users and groups entries + * Enhancement #4200: Multiple fixes for Ceph driver - Entries in the rest user and group drivers do not expire. This means that old users/groups that - have been deleted are still in cache. Now an expiration of `fetch interval + 1` hours has been - set. + * Avoid usage/creation of user homes when they are disabled in the config * Simplify the regular + uploads (not chunked) * Avoid creation of shadow folders at the root if they are already there * + Clean up the chunked upload * Fix panic on shutdown - https://github.com/cs3org/reva/pull/4121 + https://github.com/cs3org/reva/pull/4200 - * Enhancement #4162: Disable sharing on a storage provider + * Enhancement #4185: Refurbish the grpc and https plugins for eos - Added a GRPC interceptor that disable sharing permissions on a storage provider. + This enhancement refurbishes the grpc and https plugins for eos - https://github.com/cs3org/reva/pull/4162 + https://github.com/cs3org/reva/pull/4185 - * Enhancement #4163: Disable trashbin on a storage provider + * Enhancement #4166: Add better observability with metrics and traces - Added a GRPC interceptor that disable the trashbin on a storage provider. + Adds prometheus collectors that can be registered dynamically and also refactors the http and + grpc clients and servers to propage trace info. - https://github.com/cs3org/reva/pull/4163 + https://github.com/cs3org/reva/pull/4166 - * Enhancement #4164: Disable versions on a storage provider + * Enhancement #4195: Support incoming OCM 1.0 shares - Added a GRPC interceptor that disable the versions on a storage provider. + OCM 1.0 payloads are now supported as incoming shares, and converted to the OCM 1.1 format for + persistency and further processing. Outgoing shares are still only OCM 1.1. - https://github.com/cs3org/reva/pull/4164 + https://github.com/cs3org/reva/pull/4195 - * Enhancement #4084: Implementation of an app provider for Overleaf + * Enhancement #4189: Support full URL endpoints in ocm-provider - This PR adds an app provider for Overleaf as a standalone http service. + This patch enables a reva server to properly show any configured endpoint route in all relevant + properties exposed by /ocm-provider. This allows reverse proxy configurations of the form + https://server/route to be supported for the OCM discovery mechanism. - The app provider currently consists of support for the export to Overleaf feature, which when - called returns a URL to Overleaf that prompts Overleaf to download the appropriate resource - making use of the Archiver service, and upload the files to a user's Overleaf account. + https://github.com/cs3org/reva/pull/4189 - https://github.com/cs3org/reva/pull/4084 + * Enhancement #4186: Fixes in the reference configuration for ScienceMesh - * Enhancement #4114: List all the registered plugins + Following the successful onboarding of CESNET, this PR brings some improvements and fixes to + the reference configuration, as well as some adaptation to the itegration tests. - https://github.com/cs3org/reva/pull/4114 + https://github.com/cs3org/reva/pull/4186 + https://github.com/cs3org/reva/pull/4184 + https://github.com/cs3org/reva/pull/4183 - * Enhancement #4115: All required features and fixes for the OC/NC ScienceMesh apps + * Enhancement #4191: Add metrics service to ScienceMesh example config - This PR includes all necessary code in Reva to interface with the ScienceMesh apps in OC and NC + Adds the metrics http service configuration to the example config file of a ScienceMesh site. + Having this service configured is a prerequisite for successfull Prometheus-based + ScienceMesh sites metrics scraping. - https://github.com/cs3org/reva/pull/4115 + https://github.com/cs3org/reva/pull/4191 diff --git a/changelog/unreleased/ceph-locks.md b/changelog/unreleased/ceph-locks.md new file mode 100644 index 0000000000..75e9be878f --- /dev/null +++ b/changelog/unreleased/ceph-locks.md @@ -0,0 +1,5 @@ +Enhancement: implementation of Locks for the CephFS driver + +This PR brings CS3APIs Locks for CephFS + +https://github.com/cs3org/reva/pull/4280 diff --git a/changelog/unreleased/public-share-owner.md b/changelog/unreleased/public-share-owner.md index ab411bcb62..cf0e372e85 100644 --- a/changelog/unreleased/public-share-owner.md +++ b/changelog/unreleased/public-share-owner.md @@ -1,3 +1,3 @@ -Bugfix: access public links to projects as owner +Bugfix: Fixes registration and naming of services -https://github.com/cs3org/reva/pull/4196 +https://github.com/cs3org/reva/pull/4287 \ No newline at end of file diff --git a/docs/content/en/docs/changelog/1.27.0/_index.md b/docs/content/en/docs/changelog/1.27.0/_index.md new file mode 100644 index 0000000000..9274c01449 --- /dev/null +++ b/docs/content/en/docs/changelog/1.27.0/_index.md @@ -0,0 +1,120 @@ + +--- +title: "v1.27.0" +linkTitle: "v1.27.0" +weight: 40 +description: > + Changelog for Reva v1.27.0 (2023-10-19) +--- + +Changelog for reva 1.27.0 (2023-10-19) +======================================= + +The following sections list the changes in reva 1.27.0 relevant to +reva users. The changes are ordered by importance. + +Summary +------- + + * Fix #4196: Access public links to projects as owner + * Enh #4266: Improve authentication routing logic + * Enh #4212: CERNBox cleanup + * Enh #4199: Dynamic storage provider + * Enh #4264: Implement eos-compliant app locks + * Enh #4200: Multiple fixes for Ceph driver + * Enh #4185: Refurbish the grpc and https plugins for eos + * Enh #4166: Add better observability with metrics and traces + * Enh #4195: Support incoming OCM 1.0 shares + * Enh #4189: Support full URL endpoints in ocm-provider + * Enh #4186: Fixes in the reference configuration for ScienceMesh + * Enh #4191: Add metrics service to ScienceMesh example config + +Details +------- + + * Bugfix #4196: Access public links to projects as owner + + https://github.com/cs3org/reva/pull/4196 + + * Enhancement #4266: Improve authentication routing logic + + Provides a safer approach to route requests, both in HTTP and gRPC land when authentication is + needed. + + https://github.com/cs3org/reva/pull/4266 + + * Enhancement #4212: CERNBox cleanup + + Remove from the codebase all the cernbox specific code + + https://github.com/cs3org/reva/pull/4212 + + * Enhancement #4199: Dynamic storage provider + + Add a new storage provider that can globally route to other providers. This provider uses a + routing table in the database containing `path` - `mountid` pairs, and a mapping `mountid` - + `address` in the config. It also support rewriting paths for resolution (to enable more + complex cases). + + https://github.com/cs3org/reva/pull/4199 + + * Enhancement #4264: Implement eos-compliant app locks + + The eosfs package now uses the app locks provided by eos + + https://github.com/cs3org/reva/pull/4264 + + * Enhancement #4200: Multiple fixes for Ceph driver + + * Avoid usage/creation of user homes when they are disabled in the config * Simplify the regular + uploads (not chunked) * Avoid creation of shadow folders at the root if they are already there * + Clean up the chunked upload * Fix panic on shutdown + + https://github.com/cs3org/reva/pull/4200 + + * Enhancement #4185: Refurbish the grpc and https plugins for eos + + This enhancement refurbishes the grpc and https plugins for eos + + https://github.com/cs3org/reva/pull/4185 + + * Enhancement #4166: Add better observability with metrics and traces + + Adds prometheus collectors that can be registered dynamically and also refactors the http and + grpc clients and servers to propage trace info. + + https://github.com/cs3org/reva/pull/4166 + + * Enhancement #4195: Support incoming OCM 1.0 shares + + OCM 1.0 payloads are now supported as incoming shares, and converted to the OCM 1.1 format for + persistency and further processing. Outgoing shares are still only OCM 1.1. + + https://github.com/cs3org/reva/pull/4195 + + * Enhancement #4189: Support full URL endpoints in ocm-provider + + This patch enables a reva server to properly show any configured endpoint route in all relevant + properties exposed by /ocm-provider. This allows reverse proxy configurations of the form + https://server/route to be supported for the OCM discovery mechanism. + + https://github.com/cs3org/reva/pull/4189 + + * Enhancement #4186: Fixes in the reference configuration for ScienceMesh + + Following the successful onboarding of CESNET, this PR brings some improvements and fixes to + the reference configuration, as well as some adaptation to the itegration tests. + + https://github.com/cs3org/reva/pull/4186 + https://github.com/cs3org/reva/pull/4184 + https://github.com/cs3org/reva/pull/4183 + + * Enhancement #4191: Add metrics service to ScienceMesh example config + + Adds the metrics http service configuration to the example config file of a ScienceMesh site. + Having this service configured is a prerequisite for successfull Prometheus-based + ScienceMesh sites metrics scraping. + + https://github.com/cs3org/reva/pull/4191 + + diff --git a/go.mod b/go.mod index b5df636545..e26cd9a678 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/juliangruber/go-intersect v1.1.0 github.com/mattn/go-sqlite3 v1.14.17 github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b - github.com/mileusna/useragent v1.2.1 + github.com/mileusna/useragent v1.3.4 github.com/mitchellh/mapstructure v1.5.0 github.com/nats-io/nats-server/v2 v2.9.19 github.com/nats-io/nats-streaming-server v0.25.5 @@ -48,9 +48,9 @@ require ( github.com/onsi/gomega v1.27.10 github.com/pkg/errors v0.9.1 github.com/prometheus/alertmanager v0.26.0 - github.com/prometheus/client_golang v1.16.0 - github.com/rs/cors v1.9.0 - github.com/rs/zerolog v1.28.0 + github.com/prometheus/client_golang v1.17.0 + github.com/rs/cors v1.10.1 + github.com/rs/zerolog v1.31.0 github.com/sethvargo/go-password v0.2.0 github.com/stretchr/testify v1.8.4 github.com/studio-b12/gowebdav v0.9.0 @@ -59,17 +59,16 @@ require ( github.com/wk8/go-ordered-map v1.0.0 go-micro.dev/v4 v4.3.1-0.20211108085239-0c2041e43908 go.opencensus.io v0.24.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 - go.opentelemetry.io/otel/trace v1.18.0 - go.step.sm/crypto v0.35.0 - golang.org/x/crypto v0.13.0 - golang.org/x/oauth2 v0.11.0 - golang.org/x/sync v0.3.0 - golang.org/x/sys v0.12.0 - golang.org/x/term v0.12.0 + go.opentelemetry.io/otel/trace v1.19.0 + go.step.sm/crypto v0.36.0 + golang.org/x/crypto v0.14.0 + golang.org/x/oauth2 v0.13.0 + golang.org/x/sync v0.4.0 + golang.org/x/sys v0.13.0 + golang.org/x/term v0.13.0 golang.org/x/text v0.13.0 - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 - google.golang.org/grpc v1.58.0 + google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb + google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 gotest.tools v2.2.0+incompatible ) @@ -88,14 +87,11 @@ require ( github.com/dolthub/vitess v0.0.0-20221031111135-9aad77e7b39f // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.13.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-kit/kit v0.10.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/errors v0.20.4 // indirect github.com/go-openapi/strfmt v0.21.7 // indirect github.com/gocraft/dbr/v2 v2.7.2 // indirect @@ -114,8 +110,8 @@ require ( github.com/klauspost/compress v1.16.7 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lestrrat-go/strftime v1.0.4 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect @@ -135,9 +131,9 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pkg/term v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect @@ -146,14 +142,13 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect - go.opentelemetry.io/otel v1.18.0 // indirect - go.opentelemetry.io/otel/metric v1.18.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.15.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 596abae755..10628f7bc3 100644 --- a/go.sum +++ b/go.sum @@ -945,7 +945,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= @@ -1020,8 +1020,6 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -1079,11 +1077,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -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-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= @@ -1438,8 +1433,9 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -1450,8 +1446,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -1475,8 +1472,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/mileusna/useragent v1.2.1 h1:p3RJWhi3LfuI6BHdddojREyK3p6qX67vIfOVMnUIVr0= -github.com/mileusna/useragent v1.2.1/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= +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/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= @@ -1629,8 +1626,9 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1638,8 +1636,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1666,8 +1664,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= -github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -1680,11 +1678,11 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= -github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= @@ -1839,19 +1837,15 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48= -go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= -go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= -go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= -go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= -go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= -go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.step.sm/crypto v0.35.0 h1:0N6ks5n1sdv4+biJMUTdqHjpTBKKN9zNqqBdOJIyHe4= -go.step.sm/crypto v0.35.0/go.mod h1:sBsrpVReoxmiLexbWL+vQRxZd6Gq4YBj/IRSUH+DZe4= +go.step.sm/crypto v0.36.0 h1:njrVQqjWVyCzHxqQUDprR7HXypZX8IxtOqe4IIHSjgU= +go.step.sm/crypto v0.36.0/go.mod h1:XMRbgkhoigPLaPMjZeY1VFIYXVw03Wul0+5lENF6l7c= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1899,8 +1893,9 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2041,8 +2036,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2076,8 +2071,9 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2095,8 +2091,9 @@ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2228,8 +2225,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2245,8 +2243,9 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2594,8 +2593,9 @@ google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mR google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= @@ -2615,8 +2615,9 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk= google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -2668,8 +2669,8 @@ google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGO google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/internal/grpc/interceptors/auth/auth.go b/internal/grpc/interceptors/auth/auth.go index ed42c2939e..27bb5ed447 100644 --- a/internal/grpc/interceptors/auth/auth.go +++ b/internal/grpc/interceptors/auth/auth.go @@ -98,23 +98,10 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI if utils.Skip(info.FullMethod, unprotected) { log.Debug().Str("method", info.FullMethod).Msg("skipping auth") - - // If a token is present, set it anyway, as we might need the user info - // to decide the storage provider. - tkn, ok := appctx.ContextGetToken(ctx) - if ok { - u, scopes, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, true) - if err == nil { - if blockedUsers.IsBlocked(u.Username) { - return nil, status.Errorf(codes.PermissionDenied, "user %s blocked", u.Username) - } - ctx = appctx.ContextSetUser(ctx, u) - ctx = appctx.ContextSetScopes(ctx, scopes) - } - } return handler(ctx, req) } + // from this point on, the method must be authenticated tkn, ok := appctx.ContextGetToken(ctx) if !ok || tkn == "" { @@ -137,6 +124,7 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI ctx = appctx.ContextSetScopes(ctx, scopes) return handler(ctx, req) } + return interceptor, nil } @@ -171,19 +159,6 @@ func NewStream(m map[string]interface{}, unprotected []string) (grpc.StreamServe if utils.Skip(info.FullMethod, unprotected) { log.Debug().Str("method", info.FullMethod).Msg("skipping auth") - - // If a token is present, set it anyway, as we might need the user info - // to decide the storage provider. - tkn, ok := appctx.ContextGetToken(ctx) - if ok { - u, scopes, err := dismantleToken(ctx, tkn, ss, tokenManager, conf.GatewayAddr, true) - if err == nil { - ctx = appctx.ContextSetUser(ctx, u) - ctx = appctx.ContextSetScopes(ctx, scopes) - ss = newWrappedServerStream(ctx, ss) - } - } - return handler(srv, ss) } diff --git a/internal/grpc/services/gateway/gateway.go b/internal/grpc/services/gateway/gateway.go index a85d18fdf1..7d190ef9bb 100644 --- a/internal/grpc/services/gateway/gateway.go +++ b/internal/grpc/services/gateway/gateway.go @@ -170,7 +170,42 @@ func (s *svc) Close() error { } func (s *svc) UnprotectedEndpoints() []string { - return []string{"/cs3.gateway.v1beta1.GatewayAPI"} + return []string{ + "/cs3.gateway.v1beta1.GatewayAPI/ListShare", + "/cs3.gateway.v1beta1.GatewayAPI/GetAppPassword", + "/cs3.gateway.v1beta1.GatewayAPI/AddAppProvider", + "/cs3.gateway.v1beta1.GatewayAPI/ListSupportedMimeTypes", + "/cs3.gateway.v1beta1.GatewayAPI/Authenticate", + "/cs3.gateway.v1beta1.GatewayAPI/GetAuthProvider", + "/cs3.gateway.v1beta1.GatewayAPI/ListAuthProviders", + "/cs3.gateway.v1beta1.GatewayAPI/CreateOCMCoreShare", + "/cs3.gateway.v1beta1.GatewayAPI/AcceptInvite", + "/cs3.gateway.v1beta1.GatewayAPI/GetAcceptedUser", + "/cs3.gateway.v1beta1.GatewayAPI/IsProviderAllowed", + "/cs3.gateway.v1beta1.GatewayAPI/ListAllProviders", + "/cs3.gateway.v1beta1.GatewayAPI/GetOCMShareByToken", + "/cs3.gateway.v1beta1.GatewayAPI/GetPublicShareByToken", + "/cs3.gateway.v1beta1.GatewayAPI/GetUser", + "/cs3.gateway.v1beta1.GatewayAPI/GetUserByClaim", + "/cs3.gateway.v1beta1.GatewayAPI/GetUserGroups", + + "/cs3.auth.applications.v1beta1.ApplicationsAPI/GetAppPassword", + "/cs3.app.registry.v1beta1.RegistryAPI/AddAppProvider", + "/cs3.app.registry.v1beta1.RegistryAPI/ListSupportedMimeTypes", + "/cs3.auth.provider.v1beta1.ProviderAPI/Authenticate", + "/cs3.auth.registry.v1beta1.RegistryAPI/GetAuthProvider", + "/cs3.auth.registry.v1beta1.RegistryAPI/ListAuthProviders", + "/cs3.ocm.core.v1beta1.OcmCoreAPI/CreateOCMCoreShare", + "/cs3.ocm.invite.v1beta1.InviteAPI/AcceptInvite", + "/cs3.ocm.invite.v1beta1.InviteAPI/GetAcceptedUser", + "/cs3.ocm.provider.v1beta1.ProviderAPI/IsProviderAllowed", + "/cs3.ocm.provider.v1beta1.ProviderAPI/ListAllProviders", + "/cs3.sharing.ocm.v1beta1.OcmAPI/GetOCMShareByToken", + "/cs3.sharing.link.v1beta1.LinkAPI/GetPublicShareByToken", + "/cs3.identity.user.v1beta1.UserAPI/GetUser", + "/cs3.identity.user.v1beta1.UserAPI/GetUserByClaim", + "/cs3.identity.user.v1beta1.UserAPI/GetUserGroups", + } } func getTokenManager(manager string, m map[string]map[string]interface{}) (token.Manager, error) { diff --git a/internal/http/interceptors/auth/auth.go b/internal/http/interceptors/auth/auth.go index 6062c52397..4c89466778 100644 --- a/internal/http/interceptors/auth/auth.go +++ b/internal/http/interceptors/auth/auth.go @@ -168,10 +168,8 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err log := appctx.GetLogger(r.Context()) - // For unprotected URLs, we try to authenticate the request in case some service needs it, - // but don't return any errors if it fails. if utils.Skip(r.URL.Path, unprotected) { - log.Info().Msg("skipping auth check for: " + r.URL.Path) + log.Info().Interface("unprotected", unprotected).Msg("skipping auth check for: " + r.URL.Path) } else { ctx, err := authenticateUser(w, r, conf, tokenStrategyChain, tokenManager, tokenWriter, credChain, false) if err != nil { diff --git a/internal/http/services/ocmd/ocm.go b/internal/http/services/ocmd/ocm.go index 13c901539a..f848bb6a86 100644 --- a/internal/http/services/ocmd/ocm.go +++ b/internal/http/services/ocmd/ocm.go @@ -30,7 +30,7 @@ import ( ) func init() { - global.Register("ocmd", New) + global.Register("ocm", New) } type config struct { diff --git a/pkg/preferences/loader/loader.go b/pkg/preferences/loader/loader.go index 1d0620c4a5..708427987b 100644 --- a/pkg/preferences/loader/loader.go +++ b/pkg/preferences/loader/loader.go @@ -21,5 +21,6 @@ package loader import ( // Load preferences drivers. _ "github.com/cs3org/reva/pkg/preferences/memory" + _ "github.com/cs3org/reva/pkg/preferences/sql" // Add your own here. ) diff --git a/pkg/share/manager/loader/loader.go b/pkg/share/manager/loader/loader.go index e80b82788f..daa37bd113 100644 --- a/pkg/share/manager/loader/loader.go +++ b/pkg/share/manager/loader/loader.go @@ -22,6 +22,5 @@ import ( // Load core share manager drivers. _ "github.com/cs3org/reva/pkg/share/manager/json" _ "github.com/cs3org/reva/pkg/share/manager/memory" - _ "github.com/cs3org/reva/pkg/share/manager/sql" // Add your own here. ) diff --git a/pkg/share/manager/sql/conversions.go b/pkg/share/manager/sql/conversions.go deleted file mode 100644 index a09cae34e5..0000000000 --- a/pkg/share/manager/sql/conversions.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql - -import ( - "context" - - grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" - 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" - conversions "github.com/cs3org/reva/internal/http/services/owncloud/ocs/conversions" - "github.com/cs3org/reva/pkg/rgrpc/status" - "github.com/cs3org/reva/pkg/rgrpc/todo/pool" -) - -//go:generate mockery -name UserConverter - -// DBShare stores information about user and public shares. -type DBShare struct { - ID string - UIDOwner string - UIDInitiator string - ItemStorage string - ItemSource string - ShareWith string - Token string - Expiration string - Permissions int - ShareType int - ShareName string - STime int - FileTarget string - RejectedBy string - State int -} - -// UserConverter describes an interface for converting user ids to names and back. -type UserConverter interface { - UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) - UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) -} - -// GatewayUserConverter converts usernames and ids using the gateway. -type GatewayUserConverter struct { - gwAddr string -} - -// NewGatewayUserConverter returns a instance of GatewayUserConverter. -func NewGatewayUserConverter(gwAddr string) *GatewayUserConverter { - return &GatewayUserConverter{ - gwAddr: gwAddr, - } -} - -// UserIDToUserName converts a user ID to an username. -func (c *GatewayUserConverter) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) { - gwConn, err := pool.GetGatewayServiceClient(pool.Endpoint(c.gwAddr)) - if err != nil { - return "", err - } - getUserResponse, err := gwConn.GetUser(ctx, &userpb.GetUserRequest{ - UserId: userid, - SkipFetchingUserGroups: true, - }) - if err != nil { - return "", err - } - if getUserResponse.Status.Code != rpc.Code_CODE_OK { - return "", status.NewErrorFromCode(getUserResponse.Status.Code, "gateway") - } - return getUserResponse.User.Username, nil -} - -// UserNameToUserID converts a username to an user ID. -func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) { - gwConn, err := pool.GetGatewayServiceClient(pool.Endpoint(c.gwAddr)) - if err != nil { - return nil, err - } - getUserResponse, err := gwConn.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{ - Claim: "username", - Value: username, - SkipFetchingUserGroups: true, - }) - if err != nil { - return nil, err - } - if getUserResponse.Status.Code != rpc.Code_CODE_OK { - return nil, status.NewErrorFromCode(getUserResponse.Status.Code, "gateway") - } - return getUserResponse.User.Id, nil -} - -func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) { - var granteeType int - var formattedID string - switch g.Type { - case provider.GranteeType_GRANTEE_TYPE_USER: - granteeType = 0 - var err error - formattedID, err = m.userConverter.UserIDToUserName(ctx, g.GetUserId()) - if err != nil { - return 0, "", err - } - case provider.GranteeType_GRANTEE_TYPE_GROUP: - granteeType = 1 - formattedID = formatGroupID(g.GetGroupId()) - default: - granteeType = -1 - } - return granteeType, formattedID, nil -} - -func (m *mgr) extractGrantee(ctx context.Context, t int, g string) (*provider.Grantee, error) { - var grantee provider.Grantee - switch t { - case 0: - userid, err := m.userConverter.UserNameToUserID(ctx, g) - if err != nil { - return nil, err - } - grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER - grantee.Id = &provider.Grantee_UserId{UserId: userid} - case 1: - grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP - grantee.Id = &provider.Grantee_GroupId{GroupId: extractGroupID(g)} - default: - grantee.Type = provider.GranteeType_GRANTEE_TYPE_INVALID - } - return &grantee, nil -} - -func resourceTypeToItem(r provider.ResourceType) string { - switch r { - case provider.ResourceType_RESOURCE_TYPE_FILE: - return "file" - case provider.ResourceType_RESOURCE_TYPE_CONTAINER: - return "folder" - case provider.ResourceType_RESOURCE_TYPE_REFERENCE: - return "reference" - case provider.ResourceType_RESOURCE_TYPE_SYMLINK: - return "symlink" - default: - return "" - } -} - -func sharePermToInt(p *provider.ResourcePermissions) int { - return int(conversions.RoleFromResourcePermissions(p).OCSPermissions()) -} - -func intTosharePerm(p int) (*provider.ResourcePermissions, error) { - perms, err := conversions.NewPermissions(p) - if err != nil { - return nil, err - } - - return conversions.RoleFromOCSPermissions(perms).CS3ResourcePermissions(), nil -} - -func intToShareState(g int) collaboration.ShareState { - switch g { - case 0: - return collaboration.ShareState_SHARE_STATE_ACCEPTED - case 1: - return collaboration.ShareState_SHARE_STATE_PENDING - case 2: - return collaboration.ShareState_SHARE_STATE_REJECTED - default: - return collaboration.ShareState_SHARE_STATE_INVALID - } -} - -func formatUserID(u *userpb.UserId) string { - return u.OpaqueId -} - -func formatGroupID(u *grouppb.GroupId) string { - return u.OpaqueId -} - -func extractGroupID(u string) *grouppb.GroupId { - return &grouppb.GroupId{OpaqueId: u} -} - -func (m *mgr) convertToCS3Share(ctx context.Context, s DBShare, storageMountID string) (*collaboration.Share, error) { - ts := &typespb.Timestamp{ - Seconds: uint64(s.STime), - } - permissions, err := intTosharePerm(s.Permissions) - if err != nil { - return nil, err - } - grantee, err := m.extractGrantee(ctx, s.ShareType, s.ShareWith) - if err != nil { - return nil, err - } - owner, err := m.userConverter.UserNameToUserID(ctx, s.UIDOwner) - if err != nil { - return nil, err - } - var creator *userpb.UserId - if s.UIDOwner == s.UIDInitiator { - creator = owner - } else { - creator, err = m.userConverter.UserNameToUserID(ctx, s.UIDOwner) - if err != nil { - return nil, err - } - } - return &collaboration.Share{ - Id: &collaboration.ShareId{ - OpaqueId: s.ID, - }, - ResourceId: &provider.ResourceId{ - StorageId: storageMountID + "!" + s.ItemStorage, - OpaqueId: s.ItemSource, - }, - Permissions: &collaboration.SharePermissions{Permissions: permissions}, - Grantee: grantee, - Owner: owner, - Creator: creator, - Ctime: ts, - Mtime: ts, - }, nil -} - -func (m *mgr) convertToCS3ReceivedShare(ctx context.Context, s DBShare, storageMountID string) (*collaboration.ReceivedShare, error) { - share, err := m.convertToCS3Share(ctx, s, storageMountID) - if err != nil { - return nil, err - } - var state collaboration.ShareState - if s.RejectedBy != "" { - state = collaboration.ShareState_SHARE_STATE_REJECTED - } else { - state = intToShareState(s.State) - } - return &collaboration.ReceivedShare{ - Share: share, - State: state, - }, nil -} diff --git a/pkg/share/manager/sql/mocks/UserConverter.go b/pkg/share/manager/sql/mocks/UserConverter.go deleted file mode 100644 index 48da015a80..0000000000 --- a/pkg/share/manager/sql/mocks/UserConverter.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - - userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" -) - -// UserConverter is an autogenerated mock type for the UserConverter type -type UserConverter struct { - mock.Mock -} - -// UserIDToUserName provides a mock function with given fields: ctx, userid -func (_m *UserConverter) UserIDToUserName(ctx context.Context, userid *userv1beta1.UserId) (string, error) { - ret := _m.Called(ctx, userid) - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context, *userv1beta1.UserId) string); ok { - r0 = rf(ctx, userid) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *userv1beta1.UserId) error); ok { - r1 = rf(ctx, userid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UserNameToUserID provides a mock function with given fields: ctx, username -func (_m *UserConverter) UserNameToUserID(ctx context.Context, username string) (*userv1beta1.UserId, error) { - ret := _m.Called(ctx, username) - - var r0 *userv1beta1.UserId - if rf, ok := ret.Get(0).(func(context.Context, string) *userv1beta1.UserId); ok { - r0 = rf(ctx, username) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*userv1beta1.UserId) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, username) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/pkg/share/manager/sql/sql.go b/pkg/share/manager/sql/sql.go deleted file mode 100644 index 2c156fd713..0000000000 --- a/pkg/share/manager/sql/sql.go +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql - -import ( - "context" - "database/sql" - "fmt" - "path" - "strconv" - "strings" - "time" - - 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/pkg/appctx" - "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/share" - "github.com/cs3org/reva/pkg/share/manager/registry" - "github.com/cs3org/reva/pkg/sharedconf" - "github.com/cs3org/reva/pkg/utils" - "github.com/cs3org/reva/pkg/utils/cfg" - - // Provides mysql drivers. - _ "github.com/go-sql-driver/mysql" - "github.com/pkg/errors" - "google.golang.org/genproto/protobuf/field_mask" -) - -const ( - shareTypeUser = 0 - shareTypeGroup = 1 -) - -func init() { - registry.Register("oc10-sql", NewMysql) -} - -type config struct { - GatewayAddr string `mapstructure:"gatewaysvc"` - StorageMountID string `mapstructure:"storage_mount_id"` - DBUsername string `mapstructure:"db_username"` - DBPassword string `mapstructure:"db_password"` - DBHost string `mapstructure:"db_host"` - DBPort int `mapstructure:"db_port"` - DBName string `mapstructure:"db_name"` -} - -func (c *config) ApplyDefaults() { - c.GatewayAddr = sharedconf.GetGatewaySVC(c.GatewayAddr) -} - -type mgr struct { - driver string - db *sql.DB - storageMountID string - userConverter UserConverter -} - -// NewMysql returns a new share manager connection to a mysql database. -func NewMysql(ctx context.Context, m map[string]interface{}) (share.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)) - if err != nil { - return nil, err - } - - userConverter := NewGatewayUserConverter(c.GatewayAddr) - - return New("mysql", db, c.StorageMountID, userConverter) -} - -// New returns a new Cache instance connecting to the given sql.DB. -func New(driver string, db *sql.DB, storageMountID string, userConverter UserConverter) (share.Manager, error) { - return &mgr{ - driver: driver, - db: db, - storageMountID: storageMountID, - userConverter: userConverter, - }, nil -} - -func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { - user := appctx.ContextMustGetUser(ctx) - - // do not allow share to myself or the owner if share is for a user - // TODO(labkode): should not this be caught already at the gw level? - if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && - (utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) { - return nil, errors.New("sql: owner/creator and grantee are the same") - } - - // check if share already exists. - key := &collaboration.ShareKey{ - Owner: md.Owner, - ResourceId: md.Id, - Grantee: g.Grantee, - } - _, err := m.getByKey(ctx, key) - - // share already exists - if err == nil { - return nil, errtypes.AlreadyExists(key.String()) - } - - now := time.Now().Unix() - ts := &typespb.Timestamp{ - Seconds: uint64(now), - } - - owner, err := m.userConverter.UserIDToUserName(ctx, md.Owner) - if err != nil { - return nil, err - } - shareType, shareWith, err := m.formatGrantee(ctx, g.Grantee) - if err != nil { - return nil, err - } - itemType := resourceTypeToItem(md.Type) - targetPath := path.Join("/", path.Base(md.Path)) - permissions := sharePermToInt(g.Permissions.Permissions) - 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 - } - - stmtString := "INSERT INTO oc_share (share_type,uid_owner,uid_initiator,item_type,item_source,file_source,permissions,stime,share_with,file_target) VALUES (?,?,?,?,?,?,?,?,?,?)" - stmtValues := []interface{}{shareType, owner, user.Username, itemType, 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 - } - - 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 -} - -func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { - var s *collaboration.Share - var err error - switch { - case ref.GetId() != nil: - s, err = m.getByID(ctx, ref.GetId()) - case ref.GetKey() != nil: - s, err = m.getByKey(ctx, ref.GetKey()) - default: - err = errtypes.NotFound(ref.String()) - } - - if err != nil { - return nil, err - } - - return s, nil -} - -func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { - uid := appctx.ContextMustGetUser(ctx).Username - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "DELETE FROM oc_share where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, ref.GetId().OpaqueId, uid, uid) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return err - } - owner := formatUserID(key.Owner) - query = "DELETE FROM oc_share WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid) - default: - return errtypes.NotFound(ref.String()) - } - - stmt, err := m.db.Prepare(query) - if err != nil { - return err - } - res, err := stmt.Exec(params...) - if err != nil { - return err - } - - rowCnt, err := res.RowsAffected() - if err != nil { - return err - } - if rowCnt == 0 { - return errtypes.NotFound(ref.String()) - } - return nil -} - -func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) { - permissions := sharePermToInt(p.Permissions) - uid := appctx.ContextMustGetUser(ctx).Username - - var query string - params := []interface{}{} - switch { - case ref.GetId() != nil: - query = "update oc_share set permissions=?,stime=? where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, uid, uid) - case ref.GetKey() != nil: - key := ref.GetKey() - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - owner := formatUserID(key.Owner) - query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid) - default: - return nil, errtypes.NotFound(ref.String()) - } - - stmt, err := m.db.Prepare(query) - if err != nil { - return nil, err - } - if _, err = stmt.Exec(params...); err != nil { - return nil, err - } - - return m.GetShare(ctx, ref) -} - -func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) { - uid := appctx.ContextMustGetUser(ctx).Username - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE (uid_owner=? or uid_initiator=?)" - params := []interface{}{uid, uid} - - var ( - filterQuery string - filterParams []interface{} - err error - ) - if len(filters) == 0 { - filterQuery += "(share_type=? OR share_type=?)" - params = append(params, shareTypeUser) - params = append(params, shareTypeGroup) - } else { - filterQuery, filterParams, err = translateFilters(filters) - if err != nil { - return nil, err - } - params = append(params, filterParams...) - } - - if filterQuery != "" { - query = fmt.Sprintf("%s AND (%s)", query, filterQuery) - } - - rows, err := m.db.Query(query, params...) - if err != nil { - return nil, err - } - defer rows.Close() - - var s DBShare - shares := []*collaboration.Share{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - continue - } - share, err := m.convertToCS3Share(ctx, s, m.storageMountID) - if err != nil { - return nil, err - } - shares = append(shares, share) - } - if err = rows.Err(); err != nil { - return nil, err - } - - return shares, 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 := user.Username - - params := []interface{}{uid, uid, uid} - for _, v := range user.Groups { - params = append(params, v) - } - - homeConcat := "" - if m.driver == "mysql" { // mysql upsert - homeConcat = "storages.id = CONCAT('home::', ts.uid_owner)" - } else { // sqlite3 upsert - homeConcat = "storages.id = 'home::' || ts.uid_owner" - } - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, ts.id, stime, permissions, share_type, accepted, storages.numeric_id FROM oc_share ts LEFT JOIN oc_storages storages ON " + homeConcat + " WHERE (uid_owner != ? AND uid_initiator != ?) " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - - filterQuery, filterParams, err := translateFilters(filters) - if err != nil { - return nil, err - } - params = append(params, filterParams...) - - if filterQuery != "" { - query = fmt.Sprintf("%s AND (%s)", query, filterQuery) - } - - rows, err := m.db.Query(query, params...) - if err != nil { - return nil, err - } - defer rows.Close() - - var s DBShare - shares := []*collaboration.ReceivedShare{} - for rows.Next() { - if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State, &s.ItemStorage); err != nil { - continue - } - share, err := m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) - if err != nil { - return nil, err - } - shares = append(shares, share) - } - if err = rows.Err(); err != nil { - return nil, err - } - - return shares, nil -} - -func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { - var s *collaboration.ReceivedShare - var err error - switch { - case ref.GetId() != nil: - s, err = m.getReceivedByID(ctx, ref.GetId()) - case ref.GetKey() != nil: - s, err = m.getReceivedByKey(ctx, ref.GetKey()) - default: - err = errtypes.NotFound(ref.String()) - } - - if err != nil { - return nil, err - } - - return s, nil -} - -func (m *mgr) UpdateReceivedShare(ctx context.Context, share *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask) (*collaboration.ReceivedShare, error) { - rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: share.Share.Id}}) - if err != nil { - return nil, err - } - - for i := range fieldMask.Paths { - switch fieldMask.Paths[i] { - case "state": - rs.State = share.State - // TODO case "mount_point": - default: - return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported") - } - } - - var queryAccept string - switch rs.GetState() { - case collaboration.ShareState_SHARE_STATE_REJECTED: - queryAccept = "update oc_share set accepted=2 where id=?" - case collaboration.ShareState_SHARE_STATE_ACCEPTED: - queryAccept = "update oc_share set accepted=0 where id=?" - } - - if queryAccept != "" { - stmt, err := m.db.Prepare(queryAccept) - if err != nil { - return nil, err - } - _, err = stmt.Exec(rs.Share.Id.OpaqueId) - if err != nil { - return nil, err - } - } - - return rs, nil -} - -func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) { - uid := appctx.ContextMustGetUser(ctx).Username - s := DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, stime, permissions, share_type FROM oc_share WHERE id=? AND (uid_owner=? or uid_initiator=?)" - if err := m.db.QueryRow(query, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } - return nil, err - } - return m.convertToCS3Share(ctx, s, m.storageMountID) -} - -func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { - owner, err := m.userConverter.UserIDToUserName(ctx, key.Owner) - if err != nil { - return nil, err - } - uid := appctx.ContextMustGetUser(ctx).Username - - s := DBShare{} - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" - if err = m.db.QueryRow(query, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } - return nil, err - } - return m.convertToCS3Share(ctx, s, m.storageMountID) -} - -func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.ReceivedShare, error) { - user := appctx.ContextMustGetUser(ctx) - uid := user.Username - - params := []interface{}{id.OpaqueId, uid} - for _, v := range user.Groups { - params = append(params, v) - } - - s := DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, stime, permissions, share_type, accepted FROM oc_share ts WHERE ts.id=? " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(id.OpaqueId) - } - return nil, err - } - return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) -} - -func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.ReceivedShare, error) { - user := appctx.ContextMustGetUser(ctx) - uid := user.Username - - shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee) - if err != nil { - return nil, err - } - params := []interface{}{uid, formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith} - for _, v := range user.Groups { - params = append(params, v) - } - - s := DBShare{} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(item_source, '') as item_source, ts.id, stime, permissions, share_type, accepted FROM oc_share ts WHERE uid_owner=? AND item_source=? AND share_type=? AND share_with=? " - if len(user.Groups) > 0 { - query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" - } else { - query += "AND (share_with=?)" - } - - if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { - if err == sql.ErrNoRows { - return nil, errtypes.NotFound(key.String()) - } - return nil, err - } - return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID) -} - -func granteeTypeToShareType(granteeType provider.GranteeType) int { - switch granteeType { - case provider.GranteeType_GRANTEE_TYPE_USER: - return shareTypeUser - case provider.GranteeType_GRANTEE_TYPE_GROUP: - return shareTypeGroup - } - return -1 -} - -// translateFilters translates the filters to sql queries. -func translateFilters(filters []*collaboration.Filter) (string, []interface{}, error) { - var ( - filterQuery string - params []interface{} - ) - - groupedFilters := share.GroupFiltersByType(filters) - // 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, filters := range groupedFilters { - switch filterType { - case collaboration.Filter_TYPE_RESOURCE_ID: - filterQuery += "(" - for i, f := range filters { - filterQuery += "item_source=?" - params = append(params, f.GetResourceId().OpaqueId) - - if i != len(filters)-1 { - filterQuery += " OR " - } - } - filterQuery += ")" - case collaboration.Filter_TYPE_GRANTEE_TYPE: - filterQuery += "(" - for i, f := range filters { - filterQuery += "share_type=?" - params = append(params, granteeTypeToShareType(f.GetGranteeType())) - - if i != len(filters)-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(groupedFilters)-1 { - filterQuery += " AND " - } - filterCounter++ - } - return filterQuery, params, nil -} diff --git a/pkg/share/manager/sql/sql_suite_test.go b/pkg/share/manager/sql/sql_suite_test.go deleted file mode 100644 index e3c6fbd8e9..0000000000 --- a/pkg/share/manager/sql/sql_suite_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestSql(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Sql Suite") -} diff --git a/pkg/share/manager/sql/sql_test.go b/pkg/share/manager/sql/sql_test.go deleted file mode 100644 index 176e75eb66..0000000000 --- a/pkg/share/manager/sql/sql_test.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package sql_test - -import ( - "context" - "database/sql" - "os" - - user "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" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/share" - sqlmanager "github.com/cs3org/reva/pkg/share/manager/sql" - mocks "github.com/cs3org/reva/pkg/share/manager/sql/mocks" - _ "github.com/mattn/go-sqlite3" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" - "google.golang.org/protobuf/types/known/fieldmaskpb" -) - -var _ = Describe("SQL manager", func() { - var ( - mgr share.Manager - ctx context.Context - testDBFile *os.File - - loginAs = func(user *user.User) { - ctx = appctx.ContextSetUser(context.Background(), user) - } - admin = &user.User{ - Id: &user.UserId{ - Idp: "idp", - OpaqueId: "userid", - Type: user.UserType_USER_TYPE_PRIMARY, - }, - Username: "admin", - } - otherUser = &user.User{ - Id: &user.UserId{ - Idp: "idp", - OpaqueId: "userid", - Type: user.UserType_USER_TYPE_PRIMARY, - }, - Username: "einstein", - } - - shareRef = &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: "1", - }, - }} - ) - - AfterEach(func() { - os.Remove(testDBFile.Name()) - }) - - BeforeEach(func() { - var err error - testDBFile, err = os.CreateTemp("", "example") - Expect(err).ToNot(HaveOccurred()) - - dbData, err := os.ReadFile("test.db") - Expect(err).ToNot(HaveOccurred()) - - _, err = testDBFile.Write(dbData) - Expect(err).ToNot(HaveOccurred()) - err = testDBFile.Close() - Expect(err).ToNot(HaveOccurred()) - - sqldb, err := sql.Open("sqlite3", testDBFile.Name()) - Expect(err).ToNot(HaveOccurred()) - - userConverter := &mocks.UserConverter{} - userConverter.On("UserIDToUserName", mock.Anything, mock.Anything).Return("username", nil) - userConverter.On("UserNameToUserID", mock.Anything, mock.Anything).Return( - func(_ context.Context, username string) *user.UserId { - return &user.UserId{ - OpaqueId: username, - } - }, - func(_ context.Context, username string) error { return nil }) - mgr, err = sqlmanager.New("sqlite3", sqldb, "abcde", userConverter) - Expect(err).ToNot(HaveOccurred()) - - loginAs(admin) - }) - - It("creates manager instances", func() { - Expect(mgr).ToNot(BeNil()) - }) - - Describe("GetShare", func() { - It("returns the share", func() { - share, err := mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - }) - - It("returns an error if the share does not exis", func() { - share, err := mgr.GetShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: "2", - }, - }}) - Expect(err).To(HaveOccurred()) - Expect(share).To(BeNil()) - }) - }) - - Describe("Share", func() { - It("creates a share", func() { - grant := &collaboration.ShareGrant{ - Grantee: &provider.Grantee{ - Type: provider.GranteeType_GRANTEE_TYPE_USER, - Id: &provider.Grantee_UserId{UserId: &user.UserId{ - OpaqueId: "someone", - }}, - }, - Permissions: &collaboration.SharePermissions{ - Permissions: &provider.ResourcePermissions{ - GetPath: true, - InitiateFileDownload: true, - ListFileVersions: true, - ListContainer: true, - Stat: true, - }, - }, - } - info := &provider.ResourceInfo{ - Id: &provider.ResourceId{ - StorageId: "/", - OpaqueId: "something", - }, - } - share, err := mgr.Share(ctx, info, grant) - - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.Id.OpaqueId).To(Equal("2")) - }) - }) - - Describe("ListShares", func() { - It("lists shares", func() { - shares, err := mgr.ListShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - - shares, err = mgr.ListShares(ctx, []*collaboration.Filter{ - share.ResourceIDFilter(&provider.ResourceId{ - StorageId: "/", - OpaqueId: "somethingElse", - }), - }) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(0)) - }) - }) - - Describe("ListReceivedShares", func() { - It("lists received shares", func() { - loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - }) - }) - - Describe("GetReceivedShare", func() { - It("returns the received share", func() { - loginAs(otherUser) - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - }) - }) - - Describe("UpdateReceivedShare", func() { - It("returns an error when no valid field is set in the mask", func() { - loginAs(otherUser) - - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_ACCEPTED)) - - share.State = collaboration.ShareState_SHARE_STATE_REJECTED - _, err = mgr.UpdateReceivedShare(ctx, share, &fieldmaskpb.FieldMask{Paths: []string{"foo"}}) - Expect(err).To(HaveOccurred()) - }) - - It("updates the state when the state is set in the mask", func() { - loginAs(otherUser) - - share, err := mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_ACCEPTED)) - - share.State = collaboration.ShareState_SHARE_STATE_REJECTED - share, err = mgr.UpdateReceivedShare(ctx, share, &fieldmaskpb.FieldMask{Paths: []string{"state"}}) - Expect(err).ToNot(HaveOccurred()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_REJECTED)) - - share, err = mgr.GetReceivedShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share).ToNot(BeNil()) - Expect(share.State).To(Equal(collaboration.ShareState_SHARE_STATE_REJECTED)) - }) - }) - - Describe("Unshare", func() { - It("deletes shares", func() { - loginAs(otherUser) - shares, err := mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(1)) - - loginAs(admin) - err = mgr.Unshare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{ - Id: &collaboration.ShareId{ - OpaqueId: shares[0].Share.Id.OpaqueId, - }, - }}) - Expect(err).ToNot(HaveOccurred()) - - loginAs(otherUser) - shares, err = mgr.ListReceivedShares(ctx, []*collaboration.Filter{}) - Expect(err).ToNot(HaveOccurred()) - Expect(len(shares)).To(Equal(0)) - }) - }) - - Describe("UpdateShare", func() { - It("updates permissions", func() { - share, err := mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeTrue()) - - share, err = mgr.UpdateShare(ctx, shareRef, &collaboration.SharePermissions{ - Permissions: &provider.ResourcePermissions{ - InitiateFileUpload: true, - RestoreFileVersion: true, - RestoreRecycleItem: true, - }}) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeFalse()) - - share, err = mgr.GetShare(ctx, shareRef) - Expect(err).ToNot(HaveOccurred()) - Expect(share.Permissions.Permissions.Delete).To(BeFalse()) - }) - }) -}) diff --git a/pkg/share/manager/sql/test.db b/pkg/share/manager/sql/test.db deleted file mode 100644 index fba76fdcc4..0000000000 Binary files a/pkg/share/manager/sql/test.db and /dev/null differ diff --git a/pkg/storage/fs/cephfs/cephfs.go b/pkg/storage/fs/cephfs/cephfs.go index e9e175aff4..bdc4bf73cb 100644 --- a/pkg/storage/fs/cephfs/cephfs.go +++ b/pkg/storage/fs/cephfs/cephfs.go @@ -23,20 +23,25 @@ package cephfs import ( "context" + b64 "encoding/base64" + "encoding/json" "fmt" + "hash/fnv" "io" "net/url" "os" "path/filepath" "strconv" "strings" + "time" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/storage" "github.com/cs3org/reva/pkg/storage/fs/registry" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/pkg/errors" ) @@ -49,6 +54,7 @@ const ( xattrRef = xattrTrustedNs + "ref" xattrUserNs = "user." snap = ".snap" + xattrLock = xattrUserNs + "reva.lockpayload" ) type cephfs struct { @@ -112,7 +118,7 @@ func (fs *cephfs) CreateHome(ctx context.Context) (err error) { user := fs.makeUser(ctx) // Stop createhome from running the whole thing because it is called multiple times - if _, err = fs.adminConn.adminMount.Statx(user.home, cephfs2.StatxMode, 0); err == nil { + if _, err = fs.adminConn.adminMount.Statx(user.home, goceph.StatxMode, 0); err == nil { return } @@ -221,7 +227,7 @@ func (fs *cephfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []s user.op(func(cv *cacheVal) { var stat Statx - if stat, err = cv.mount.Statx(path, cephfs2.StatxBasicStats, 0); err != nil { + if stat, err = cv.mount.Statx(path, goceph.StatxBasicStats, 0); err != nil { return } ri, err = user.fileAsResourceInfo(cv, path, stat, mdKeys) @@ -245,15 +251,15 @@ func (fs *cephfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKey } user.op(func(cv *cacheVal) { - var dir *cephfs2.Directory + var dir *goceph.Directory if dir, err = cv.mount.OpenDir(path); err != nil { return } defer closeDir(dir) - var entry *cephfs2.DirEntryPlus + var entry *goceph.DirEntryPlus var ri *provider.ResourceInfo - for entry, err = dir.ReadDirPlus(cephfs2.StatxBasicStats, 0); entry != nil && err == nil; entry, err = dir.ReadDirPlus(cephfs2.StatxBasicStats, 0) { + for entry, err = dir.ReadDirPlus(goceph.StatxBasicStats, 0); entry != nil && err == nil; entry, err = dir.ReadDirPlus(goceph.StatxBasicStats, 0) { if fs.conf.HiddenDirs[entry.Name()] { continue } @@ -306,7 +312,7 @@ func (fs *cephfs) ListRevisions(ctx context.Context, ref *provider.Reference) (f err = errtypes.PermissionDenied("cephfs: cannot download under the virtual share folder") return } - var dir *cephfs2.Directory + var dir *goceph.Directory if dir, err = cv.mount.OpenDir(".snap"); err != nil { return } @@ -325,7 +331,7 @@ func (fs *cephfs) ListRevisions(ctx context.Context, ref *provider.Reference) (f if e != nil { continue } - stat, e = cv.mount.Statx(revPath, cephfs2.StatxMtime|cephfs2.StatxSize, 0) + stat, e = cv.mount.Statx(revPath, goceph.StatxMtime|goceph.StatxSize, 0) if e != nil { continue } @@ -372,7 +378,7 @@ func (fs *cephfs) RestoreRevision(ctx context.Context, ref *provider.Reference, return } - var src, dst *cephfs2.File + var src, dst *goceph.File if src, err = cv.mount.Open(revPath, os.O_RDONLY, 0); err != nil { return } @@ -575,7 +581,7 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error } user.op(func(cv *cacheVal) { - var file *cephfs2.File + var file *goceph.File defer closeFile(file) if file, err = cv.mount.Open(path, os.O_CREATE|os.O_WRONLY, fs.conf.FilePerms); err != nil { return @@ -615,18 +621,204 @@ func (fs *cephfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateSt return nil, errtypes.NotSupported("unimplemented") } +var fnvHash = fnv.New32a() + +func getHash(s string) uint64 { + fnvHash.Write([]byte(s)) + defer fnvHash.Reset() + return uint64(fnvHash.Sum32()) +} + +func encodeLock(l *provider.Lock) string { + data, _ := json.Marshal(l) + return b64.StdEncoding.EncodeToString(data) +} + +func decodeLock(content string) (*provider.Lock, error) { + d, err := b64.StdEncoding.DecodeString(content) + if err != nil { + return nil, err + } + + l := &provider.Lock{} + if err = json.Unmarshal(d, l); err != nil { + return nil, err + } + + return l, nil +} + func (fs *cephfs) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { - return errtypes.NotSupported("unimplemented") + user := fs.makeUser(ctx) + path, err := user.resolveRef(ref) + if err != nil { + return getRevaError(err) + } + + op := goceph.LockEX + if lock.Type == provider.LockType_LOCK_TYPE_SHARED { + op = goceph.LockSH + } + + user.op(func(cv *cacheVal) { + var file *goceph.File + defer closeFile(file) + if file, err = cv.mount.Open(path, os.O_RDWR, fs.conf.FilePerms); err != nil { + return + } + + err = file.Flock(op|goceph.LockNB, getHash(lock.AppName)) + }) + + if err == nil { + // ok, we got the flock, now also store the related lock metadata + md := &provider.ArbitraryMetadata{ + Metadata: map[string]string{ + xattrLock: encodeLock(lock), + }, + } + return fs.SetArbitraryMetadata(ctx, ref, md) + } + + return getRevaError(err) } func (fs *cephfs) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { - return nil, errtypes.NotSupported("unimplemented") + user := fs.makeUser(ctx) + path, err := user.resolveRef(ref) + if err != nil { + return nil, getRevaError(err) + } + + var l *provider.Lock + user.op(func(cv *cacheVal) { + buf, errXattr := cv.mount.GetXattr(path, xattrLock) + if errXattr == nil { + if l, err = decodeLock(string(buf)); err != nil { + err = errors.Wrap(err, "malformed lock payload") + return + } + } + + var file *goceph.File + defer closeFile(file) + if file, err = cv.mount.Open(path, os.O_RDWR, fs.conf.FilePerms); err != nil { + // try and open with read-only permissions: if this succeeds, we just return + // the metadata as is, otherwise we return the error on Open() + if file, err = cv.mount.Open(path, os.O_RDONLY, fs.conf.FilePerms); err != nil { + l = nil + } + return + } + + if err = file.Flock(goceph.LockEX|goceph.LockNB, 0); err == nil { + // success means the file was not locked, drop related metadata if present + if l != nil { + fs.UnsetArbitraryMetadata(ctx, ref, []string{xattrLock}) + l = nil + } + file.Flock(goceph.LockUN|goceph.LockNB, 0) + err = errtypes.NotFound("file was not locked") + return + } + + if errXattr != nil { + // error here means we have a "foreign" flock with no CS3 metadata + err = nil + l = new(provider.Lock) + l.AppName = "External" + return + } + + if time.Unix(int64(l.Expiration.Seconds), 0).After(time.Now()) { + // the lock expired, drop + fs.UnsetArbitraryMetadata(ctx, ref, []string{xattrLock}) + file.Flock(goceph.LockUN|goceph.LockNB, getHash(l.AppName)) + err = errtypes.NotFound("file was not locked") + l = nil + } + return + }) + + return l, getRevaError(err) } -func (fs *cephfs) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error { - return errtypes.NotSupported("unimplemented") +// TODO(lopresti) part of this logic is duplicated from eosfs.go, should be factored out +func sameHolder(l1, l2 *provider.Lock) bool { + same := true + if l1.User != nil || l2.User != nil { + same = utils.UserEqual(l1.User, l2.User) + } + if l1.AppName != "" || l2.AppName != "" { + same = l1.AppName == l2.AppName + } + return same +} + +func (fs *cephfs) RefreshLock(ctx context.Context, ref *provider.Reference, newLock *provider.Lock, existingLockID string) error { + oldLock, err := fs.GetLock(ctx, ref) + if err != nil { + switch err.(type) { + case errtypes.NotFound: + // the lock does not exist + return errtypes.BadRequest("file was not locked") + default: + return err + } + } + + // check if the holder is the same of the new lock + if !sameHolder(oldLock, newLock) { + return errtypes.BadRequest("caller does not hold the lock") + } + + if existingLockID != "" && oldLock.LockId != existingLockID { + return errtypes.BadRequest("lock id does not match") + } + + return fs.SetLock(ctx, ref, newLock) } func (fs *cephfs) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { - return errtypes.NotSupported("unimplemented") + user := fs.makeUser(ctx) + path, err := user.resolveRef(ref) + if err != nil { + return getRevaError(err) + } + + oldLock, err := fs.GetLock(ctx, ref) + if err != nil { + switch err.(type) { + case errtypes.NotFound: + // the lock does not exist + return errtypes.BadRequest("file not found or not locked") + default: + return err + } + } + + // check if the lock id of the lock corresponds to the stored lock + if oldLock.LockId != lock.LockId { + return errtypes.BadRequest("lock id does not match") + } + + if !sameHolder(oldLock, lock) { + return errtypes.BadRequest("caller does not hold the lock") + } + + user.op(func(cv *cacheVal) { + var file *goceph.File + defer closeFile(file) + if file, err = cv.mount.Open(path, os.O_RDWR, fs.conf.FilePerms); err != nil { + return + } + + err = file.Flock(goceph.LockUN|goceph.LockNB, getHash(lock.AppName)) + }) + + if err == nil { + return fs.UnsetArbitraryMetadata(ctx, ref, []string{xattrLock}) + } + + return getRevaError(err) } diff --git a/pkg/storage/fs/cephfs/chunking.go b/pkg/storage/fs/cephfs/chunking.go index fc9dbe0bbd..9ea8f37a14 100644 --- a/pkg/storage/fs/cephfs/chunking.go +++ b/pkg/storage/fs/cephfs/chunking.go @@ -32,7 +32,7 @@ import ( "strings" "time" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" "github.com/google/uuid" ) @@ -118,7 +118,7 @@ func (c *ChunkHandler) saveChunk(path string, r io.ReadCloser) (finish bool, chu chunkTempFilename := c.getChunkTempFileName() c.user.op(func(cv *cacheVal) { - var tmpFile *cephfs2.File + var tmpFile *goceph.File target := filepath.Join(c.chunkFolder, chunkTempFilename) tmpFile, err = cv.mount.Open(target, os.O_CREATE|os.O_WRONLY, c.user.fs.conf.FilePerms) defer closeFile(tmpFile) @@ -152,9 +152,9 @@ func (c *ChunkHandler) saveChunk(path string, r io.ReadCloser) (finish bool, chu // assembly the chunks when the client asks for it. numEntries := 0 c.user.op(func(cv *cacheVal) { - var dir *cephfs2.Directory - var entry *cephfs2.DirEntry - var chunkFile, assembledFile *cephfs2.File + var dir *goceph.Directory + var entry *goceph.DirEntry + var chunkFile, assembledFile *goceph.File dir, err = cv.mount.OpenDir(chunksFolderName) defer closeDir(dir) diff --git a/pkg/storage/fs/cephfs/connections.go b/pkg/storage/fs/cephfs/connections.go index 6ffb0dbb7f..117c39eade 100644 --- a/pkg/storage/fs/cephfs/connections.go +++ b/pkg/storage/fs/cephfs/connections.go @@ -34,14 +34,14 @@ import ( "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/pkg/errors" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" "github.com/dgraph-io/ristretto" "golang.org/x/sync/semaphore" ) type cacheVal struct { - perm *cephfs2.UserPerm - mount *cephfs2.MountInfo + perm *goceph.UserPerm + mount *goceph.MountInfo } //TODO: Add to cephfs obj @@ -162,7 +162,7 @@ func newAdminConn(conf *Options) *adminConn { } */ - mount, err := cephfs2.CreateFromRados(rados) + mount, err := goceph.CreateFromRados(rados) if err != nil { rados.Shutdown() return nil @@ -184,8 +184,8 @@ func newAdminConn(conf *Options) *adminConn { } func newConn(user *User) *cacheVal { - var perm *cephfs2.UserPerm - mount, err := cephfs2.CreateMountWithId(user.fs.conf.ClientID) + var perm *goceph.UserPerm + mount, err := goceph.CreateMountWithId(user.fs.conf.ClientID) if err != nil { return destroyCephConn(mount, perm) } @@ -202,7 +202,7 @@ func newConn(user *User) *cacheVal { } if user != nil { //nil creates admin conn - perm = cephfs2.NewUserPerm(int(user.UidNumber), int(user.GidNumber), []int{}) + perm = goceph.NewUserPerm(int(user.UidNumber), int(user.GidNumber), []int{}) if err = mount.SetMountPerms(perm); err != nil { return destroyCephConn(mount, perm) } diff --git a/pkg/storage/fs/cephfs/errors.go b/pkg/storage/fs/cephfs/errors.go index 02f97bc1f5..2e92575390 100644 --- a/pkg/storage/fs/cephfs/errors.go +++ b/pkg/storage/fs/cephfs/errors.go @@ -51,7 +51,7 @@ func getRevaError(err error) error { } switch err.Error() { case errNotFound: - return errtypes.NotFound("cephfs: dir entry not found") + return errtypes.NotFound("cephfs: entry not found") case errPermissionDenied: return errtypes.PermissionDenied("cephfs: permission denied") case errFileExists: diff --git a/pkg/storage/fs/cephfs/permissions.go b/pkg/storage/fs/cephfs/permissions.go index d9fc1ce9cc..e2d78de375 100644 --- a/pkg/storage/fs/cephfs/permissions.go +++ b/pkg/storage/fs/cephfs/permissions.go @@ -29,7 +29,7 @@ import ( userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/maxymania/go-system/posix_acl" @@ -65,7 +65,7 @@ const ( var op2int = map[rune]uint16{'r': 4, 'w': 2, 'x': 1} -func getPermissionSet(user *User, stat *cephfs2.CephStatx, mount Mount, path string) (perm *provider.ResourcePermissions) { +func getPermissionSet(user *User, stat *goceph.CephStatx, mount Mount, path string) (perm *provider.ResourcePermissions) { perm = &provider.ResourcePermissions{} if int64(stat.Uid) == user.UidNumber || int64(stat.Gid) == user.GidNumber { diff --git a/pkg/storage/fs/cephfs/upload.go b/pkg/storage/fs/cephfs/upload.go index 0f340e7515..d27baf8377 100644 --- a/pkg/storage/fs/cephfs/upload.go +++ b/pkg/storage/fs/cephfs/upload.go @@ -29,7 +29,7 @@ import ( "os" "path/filepath" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" @@ -170,7 +170,7 @@ func (fs *cephfs) NewUpload(ctx context.Context, info tusd.FileInfo) (upload tus // Create binary file with no content user.op(func(cv *cacheVal) { - var f *cephfs2.File + var f *goceph.File defer closeFile(f) f, err = cv.mount.Open(binPath, os.O_CREATE|os.O_WRONLY, fs.conf.FilePerms) if err != nil { @@ -234,7 +234,7 @@ func (fs *cephfs) GetUpload(ctx context.Context, id string) (fup tusd.Upload, er var stat Statx user.op(func(cv *cacheVal) { - stat, err = cv.mount.Statx(binPath, cephfs2.StatxSize, 0) + stat, err = cv.mount.Statx(binPath, goceph.StatxSize, 0) }) if err != nil { return diff --git a/pkg/storage/fs/cephfs/user.go b/pkg/storage/fs/cephfs/user.go index aa5a2723b2..605148f4a2 100644 --- a/pkg/storage/fs/cephfs/user.go +++ b/pkg/storage/fs/cephfs/user.go @@ -31,7 +31,7 @@ import ( "github.com/cs3org/reva/pkg/errtypes" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" @@ -92,7 +92,7 @@ func (user *User) op(cb callBack) { cb(val.(*cacheVal)) } -func (user *User) fileAsResourceInfo(cv *cacheVal, path string, stat *cephfs2.CephStatx, mdKeys []string) (ri *provider.ResourceInfo, err error) { +func (user *User) fileAsResourceInfo(cv *cacheVal, path string, stat *goceph.CephStatx, mdKeys []string) (ri *provider.ResourceInfo, err error) { var ( _type provider.ResourceType target string diff --git a/pkg/storage/fs/cephfs/utils.go b/pkg/storage/fs/cephfs/utils.go index db0e27c759..b54c1f03ee 100644 --- a/pkg/storage/fs/cephfs/utils.go +++ b/pkg/storage/fs/cephfs/utils.go @@ -30,33 +30,33 @@ import ( "path/filepath" "strconv" - cephfs2 "github.com/ceph/go-ceph/cephfs" + goceph "github.com/ceph/go-ceph/cephfs" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" ) // Mount type -type Mount = *cephfs2.MountInfo +type Mount = *goceph.MountInfo // Statx type -type Statx = *cephfs2.CephStatx +type Statx = *goceph.CephStatx var dirPermFull = uint32(0777) var dirPermDefault = uint32(0700) var filePermDefault = uint32(0640) -func closeDir(directory *cephfs2.Directory) { +func closeDir(directory *goceph.Directory) { if directory != nil { _ = directory.Close() } } -func closeFile(file *cephfs2.File) { +func closeFile(file *goceph.File) { if file != nil { _ = file.Close() } } -func destroyCephConn(mt Mount, perm *cephfs2.UserPerm) *cacheVal { +func destroyCephConn(mt Mount, perm *goceph.UserPerm) *cacheVal { if perm != nil { perm.Destroy() } @@ -66,7 +66,7 @@ func destroyCephConn(mt Mount, perm *cephfs2.UserPerm) *cacheVal { return nil } -func deleteFile(mount *cephfs2.MountInfo, path string) { +func deleteFile(mount *goceph.MountInfo, path string) { _ = mount.Unlink(path) } diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index df49e0a41a..b9b102c2af 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -73,17 +73,16 @@ const ( UserAttr ) -// LockPayloadKey is the key in the xattr for lock payload. -const LockPayloadKey = "reva.lock.payload" +// EosLockKey is the key in the xattrs known by EOS to enforce a lock. +const EosLockKey = "app.lock" -// LockExpirationKey is the key in the xattr for lock expiration. -const LockExpirationKey = "reva.lock.expiration" - -// LockTypeKey is the key in the xattr for lock payload. -const LockTypeKey = "reva.lock.type" +// LockPayloadKey is the key in the xattrs used to store the lock payload. +const LockPayloadKey = "reva.lockpayload" var hiddenReg = regexp.MustCompile(`\.sys\..#.`) +var eosLockReg = regexp.MustCompile(`expires:\d+,type:[a-z]+,owner:.+:.+`) + func (c *Config) ApplyDefaults() { c.Namespace = path.Clean(c.Namespace) if !strings.HasPrefix(c.Namespace, "/") { @@ -527,7 +526,7 @@ func (fs *eosfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Referen } // do not allow to set a lock key attr - if k == LockPayloadKey || k == LockExpirationKey || k == LockTypeKey { + if k == LockPayloadKey || k == EosLockKey { return errtypes.BadRequest(fmt.Sprintf("eosfs: key %s not allowed", k)) } @@ -578,78 +577,27 @@ func (fs *eosfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refer return nil } -func (fs *eosfs) getLockExpiration(ctx context.Context, auth eosclient.Authorization, path string) (*types.Timestamp, bool, error) { - expiration, err := fs.c.GetAttr(ctx, auth, "sys."+LockExpirationKey, path) - if err != nil { - // since the expiration is optional, if we do not find it in the attr - // just return a nil value, without reporting the error - if _, ok := err.(errtypes.NotFound); ok { - return nil, true, nil - } - return nil, false, err - } - // the expiration value should be unix time encoded - unixTime, err := strconv.ParseInt(expiration.Val, 10, 64) - if err != nil { - return nil, false, errors.Wrap(err, "eosfs: error converting unix time") - } - t := time.Unix(unixTime, 0) - timestamp := &types.Timestamp{ - Seconds: uint64(unixTime), - } - return timestamp, t.After(time.Now()), nil -} - -func (fs *eosfs) getLockContent(ctx context.Context, auth eosclient.Authorization, path string, expiration *types.Timestamp) (*provider.Lock, error) { - t, err := fs.c.GetAttr(ctx, auth, "sys."+LockTypeKey, path) - if err != nil { - return nil, err - } - lockType, err := strconv.ParseInt(t.Val, 10, 32) - if err != nil { - return nil, errors.Wrap(err, "eosfs: error decoding lock type") - } - - d, err := fs.c.GetAttr(ctx, auth, "sys."+LockPayloadKey, path) +func (fs *eosfs) getLockPayloads(ctx context.Context, auth eosclient.Authorization, path string) (string, string, error) { + data, err := fs.c.GetAttr(ctx, auth, "sys."+LockPayloadKey, path) if err != nil { - return nil, err + return "", "", err } - data, err := b64.StdEncoding.DecodeString(d.Val) - if err != nil { - return nil, err - } - l := new(provider.Lock) - err = json.Unmarshal(data, l) + eoslock, err := fs.c.GetAttr(ctx, auth, "sys."+EosLockKey, path) if err != nil { - return nil, err + return "", "", err } - l.Type = provider.LockType(lockType) - l.Expiration = expiration - - return l, nil + return data.Val, eoslock.Val, nil } func (fs *eosfs) removeLockAttrs(ctx context.Context, auth eosclient.Authorization, path string) error { err := fs.c.UnsetAttr(ctx, auth, &eosclient.Attribute{ Type: SystemAttr, - Key: LockExpirationKey, + Key: EosLockKey, }, false, path) if err != nil { - // as the expiration time in the lock is optional - // we will discard the error if the attr is not set - if !errors.Is(err, eosclient.AttrNotExistsError) { - return errors.Wrap(err, "eosfs: error unsetting the lock expiration") - } - } - - err = fs.c.UnsetAttr(ctx, auth, &eosclient.Attribute{ - Type: SystemAttr, - Key: LockTypeKey, - }, false, path) - if err != nil { - return errors.Wrap(err, "eosfs: error unsetting the lock type") + return errors.Wrap(err, "eosfs: error unsetting the eos lock") } err = fs.c.UnsetAttr(ctx, auth, &eosclient.Attribute{ @@ -674,25 +622,26 @@ func (fs *eosfs) getLock(ctx context.Context, auth eosclient.Authorization, user return nil, errtypes.BadRequest("user has not read access on resource") } - expiration, valid, err := fs.getLockExpiration(ctx, auth, path) + d, eosl, err := fs.getLockPayloads(ctx, auth, path) if err != nil { - return nil, err + if !errors.Is(err, eosclient.AttrNotExistsError) { + return nil, errtypes.NotFound("lock not found for ref") + } } - if !valid { - // the previous lock expired + l, err := decodeLock(d, eosl) + if err != nil { + return nil, errors.Wrap(err, "eosfs: malformed lock payload") + } + + if time.Unix(int64(l.Expiration.Seconds), 0).After(time.Now()) { + // the lock expired if err := fs.removeLockAttrs(ctx, auth, path); err != nil { return nil, err } return nil, errtypes.NotFound("lock not found for ref") } - l, err := fs.getLockContent(ctx, auth, path, expiration) - if err != nil { - if !errors.Is(err, eosclient.AttrNotExistsError) { - return nil, errtypes.NotFound("lock not found for ref") - } - } return l, nil } @@ -713,43 +662,41 @@ func (fs *eosfs) GetLock(ctx context.Context, ref *provider.Reference) (*provide return nil, errors.Wrap(err, "eosfs: error getting uid and gid for user") } + // the cs3apis require to have the read permission on the resource + // to get the eventual lock. + has, err := fs.userHasReadAccess(ctx, user, ref) + if err != nil { + return nil, errors.Wrap(err, "eosfs: error checking read access to resource") + } + if !has { + return nil, errtypes.BadRequest("user has no read access on resource") + } + return fs.getLock(ctx, auth, user, path, ref) } -func (fs *eosfs) setLock(ctx context.Context, lock *provider.Lock, path string, check bool) error { +func (fs *eosfs) setLock(ctx context.Context, lock *provider.Lock, path string) error { auth, err := fs.getRootAuth(ctx) if err != nil { return err } - encodedLock, err := encodeLock(lock) + encodedLock, eosLock, err := encodeLock(lock) if err != nil { return errors.Wrap(err, "eosfs: error encoding lock") } - if lock.Expiration != nil { - // set expiration - err = fs.c.SetAttr(ctx, auth, &eosclient.Attribute{ - Type: SystemAttr, - Key: LockExpirationKey, - Val: strconv.FormatUint(lock.Expiration.Seconds, 10), - }, check, false, path) - switch { - case errors.Is(err, eosclient.AttrAlreadyExistsError): - return errtypes.BadRequest("lock already set") - case err != nil: - return err - } - } - - // set lock type + // set eos lock err = fs.c.SetAttr(ctx, auth, &eosclient.Attribute{ Type: SystemAttr, - Key: LockTypeKey, - Val: strconv.FormatUint(uint64(lock.Type), 10), + Key: EosLockKey, + Val: eosLock, }, false, false, path) - if err != nil { - return errors.Wrap(err, "eosfs: error setting lock type") + switch { + case errors.Is(err, eosclient.AttrAlreadyExistsError): + return errtypes.BadRequest("resource already locked") + case err != nil: + return errors.Wrap(err, "eosfs: error setting eos lock") } // set payload @@ -780,22 +727,6 @@ func (fs *eosfs) SetLock(ctx context.Context, ref *provider.Reference, l *provid if err != nil { return errors.Wrap(err, "eosfs: no user in ctx") } - auth, err := fs.getUserAuth(ctx, user, path) - if err != nil { - return errors.Wrap(err, "eosfs: error getting uid and gid for user") - } - - _, err = fs.getLock(ctx, auth, user, path, ref) - if err != nil { - // if the err is NotFound it is fine, otherwise we have to return - if _, ok := err.(errtypes.NotFound); !ok { - return err - } - } - if err == nil { - // the resource is already locked - return errtypes.BadRequest("resource already locked") - } // the cs3apis require to have the write permission on the resource // to set a lock. because in eos we can set attrs even if the user does @@ -806,7 +737,7 @@ func (fs *eosfs) SetLock(ctx context.Context, ref *provider.Reference, l *provid return errors.Wrap(err, fmt.Sprintf("eosfs: cannot check if user %s has write access on resource", user.Username)) } if !has { - return errtypes.PermissionDenied(fmt.Sprintf("user %s has not write access on resource", user.Username)) + return errtypes.PermissionDenied(fmt.Sprintf("user %s has no write access on resource", user.Username)) } // the user in the lock could differ from the user in the context @@ -817,11 +748,11 @@ func (fs *eosfs) SetLock(ctx context.Context, ref *provider.Reference, l *provid return errors.Wrap(err, "eosfs: cannot check if user has write access on resource") } if !has { - return errtypes.PermissionDenied(fmt.Sprintf("user %s has not write access on resource", user.Username)) + return errtypes.PermissionDenied(fmt.Sprintf("user %s has no write access on resource", user.Username)) } } - return fs.setLock(ctx, l, path, true) + return fs.setLock(ctx, l, path) } func (fs *eosfs) getUserFromID(ctx context.Context, userID *userpb.UserId) (*userpb.User, error) { @@ -868,12 +799,48 @@ func (fs *eosfs) userHasReadAccess(ctx context.Context, user *userpb.User, ref * return resInfo.PermissionSet.InitiateFileDownload, nil } -func encodeLock(l *provider.Lock) (string, error) { +func encodeLock(l *provider.Lock) (string, string, error) { data, err := json.Marshal(l) if err != nil { - return "", err + return "", "", err + } + var a string + if l.AppName != "" { + a = l.AppName + } else { + a = "*" } - return b64.StdEncoding.EncodeToString(data), nil + var u string + if l.User != nil { + u = l.User.OpaqueId + } else { + u = "*" + } + // the eos lock has hardcoded type "shared" because that's what eos supports. This is good enough + // for apps via WOPI and for checkout/checkin behavior, not for "exclusive" (no read access unless holding the lock). + return b64.StdEncoding.EncodeToString(data), + fmt.Sprintf("expires:%d,type:shared,owner:%s:%s", l.Expiration.Seconds, u, a), + nil +} + +func decodeLock(content string, eosLock string) (*provider.Lock, error) { + d, err := b64.StdEncoding.DecodeString(content) + if err != nil { + return nil, err + } + + l := new(provider.Lock) + err = json.Unmarshal(d, l) + if err != nil { + return nil, err + } + + // validate that the eosLock respect the format, otherwise raise error + if !eosLockReg.MatchString(eosLock) { + return nil, errtypes.BadRequest("eos lock payload does not match expected format: " + eosLock) + } + + return l, nil } // RefreshLock refreshes an existing lock on the given reference. @@ -904,7 +871,7 @@ func (fs *eosfs) RefreshLock(ctx context.Context, ref *provider.Reference, newLo } if existingLockID != "" && oldLock.LockId != existingLockID { - return errtypes.BadRequest("mismatching existing lock id") + return errtypes.BadRequest("lock id does not match") } path, err := fs.resolve(ctx, ref) @@ -920,10 +887,10 @@ func (fs *eosfs) RefreshLock(ctx context.Context, ref *provider.Reference, newLo return errors.Wrap(err, "eosfs: cannot check if user has write access on resource") } if !has { - return errtypes.PermissionDenied(fmt.Sprintf("user %s has not write access on resource", user.Username)) + return errtypes.PermissionDenied(fmt.Sprintf("user %s has no write access on resource", user.Username)) } - return fs.setLock(ctx, newLock, path, false) + return fs.setLock(ctx, newLock, path) } func sameHolder(l1, l2 *provider.Lock) bool { @@ -939,10 +906,6 @@ func sameHolder(l1, l2 *provider.Lock) bool { // Unlock removes an existing lock from the given reference. func (fs *eosfs) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { - if lock.Type == provider.LockType_LOCK_TYPE_SHARED { - return errtypes.NotSupported("shared lock not yet implemented") - } - oldLock, err := fs.GetLock(ctx, ref) if err != nil { switch err.(type) { @@ -975,7 +938,7 @@ func (fs *eosfs) Unlock(ctx context.Context, ref *provider.Reference, lock *prov return errors.Wrap(err, "eosfs: cannot check if user has write access on resource") } if !has { - return errtypes.PermissionDenied(fmt.Sprintf("user %s has not write access on resource", user.Username)) + return errtypes.PermissionDenied(fmt.Sprintf("user %s has no write access on resource", user.Username)) } path, err := fs.resolve(ctx, ref) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 4c7ce12133..6e94407580 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -59,7 +59,7 @@ var ( // i.e: /a/b/c/d/e contains prefix /a/b/c. func Skip(source string, prefixes []string) bool { for i := range prefixes { - if strings.HasPrefix(source, prefixes[i]) { + if strings.HasPrefix(path.Join(source, "/"), path.Join(prefixes[i], "/")) { return true } } diff --git a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml index ed9d667478..775a90f248 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cernbox-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cernboxgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cernboxhttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml index a4ee3efa9a..37f3826796 100644 --- a/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-server-cesnet-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cesnetgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cesnethttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml index 03a8a0b89b..04b6bbe7dd 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cernbox-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cernboxgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cernboxhttp_address}}" diff --git a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml index 6a12f8eb99..dea3c93878 100644 --- a/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml +++ b/tests/integration/grpc/fixtures/ocm-share/ocm-server-cesnet-http.toml @@ -7,7 +7,7 @@ gatewaysvc = "{{cesnetgw_address}}" [http] address = "{{grpc_address}}" -[http.services.ocmd] +[http.services.ocm] [http.services.sciencemesh] provider_domain = "{{cesnethttp_address}}"