diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 6096ca9e3..43c811415 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -65,11 +65,11 @@ $(HUGO): $(BINGO_DIR)/hugo.mod @echo "(re)installing $(GOBIN)/hugo-v0.115.2" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=hugo.mod -o=$(GOBIN)/hugo-v0.115.2 "github.com/gohugoio/hugo" -MOCKERY := $(GOBIN)/mockery-v2.40.1 +MOCKERY := $(GOBIN)/mockery-v2.40.2 $(MOCKERY): $(BINGO_DIR)/mockery.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/mockery-v2.40.1" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mockery.mod -o=$(GOBIN)/mockery-v2.40.1 "github.com/vektra/mockery/v2" + @echo "(re)installing $(GOBIN)/mockery-v2.40.2" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mockery.mod -o=$(GOBIN)/mockery-v2.40.2 "github.com/vektra/mockery/v2" MUTAGEN := $(GOBIN)/mutagen-v0.13.1 $(MUTAGEN): $(BINGO_DIR)/mutagen.mod diff --git a/.bingo/mockery.mod b/.bingo/mockery.mod index 9c2314f8e..6dd4aa4be 100644 --- a/.bingo/mockery.mod +++ b/.bingo/mockery.mod @@ -2,4 +2,4 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT go 1.21.6 -require github.com/vektra/mockery/v2 v2.40.1 +require github.com/vektra/mockery/v2 v2.40.2 diff --git a/.bingo/mockery.sum b/.bingo/mockery.sum index e06a7204a..f9acfc356 100644 --- a/.bingo/mockery.sum +++ b/.bingo/mockery.sum @@ -190,6 +190,8 @@ github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8 github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/vektra/mockery/v2 v2.40.1 h1:8D01rBqloDLDHKZGXkyUD9Yj5Z+oDXBqDZ+tRXYM/oA= github.com/vektra/mockery/v2 v2.40.1/go.mod h1:dPzGtjT0/Uu4hqpF6QNHwz+GLago7lq1bxdj9wHbGKo= +github.com/vektra/mockery/v2 v2.40.2 h1:JKuQ74IXibMCGKG6F0yvH/s2sNs1CHy/nGBP0We5DJ8= +github.com/vektra/mockery/v2 v2.40.2/go.mod h1:KYBZF/7sqOa86BaOZPYsoCZWEWLS90a5oBLg2pVudxY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -242,6 +244,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -395,6 +399,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/.bingo/variables.env b/.bingo/variables.env index a4673819c..7f7a6eede 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -24,7 +24,7 @@ GOVULNCHECK="${GOBIN}/govulncheck-v1.0.1" HUGO="${GOBIN}/hugo-v0.115.2" -MOCKERY="${GOBIN}/mockery-v2.40.1" +MOCKERY="${GOBIN}/mockery-v2.40.2" MUTAGEN="${GOBIN}/mutagen-v0.13.1" diff --git a/.drone.env b/.drone.env index 1a92c7738..a3f7c8506 100644 --- a/.drone.env +++ b/.drone.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=304fa011f59131657b5fc9698d0627b2b6d8c694 +WEB_COMMITID=6fca4f4f12aa58d21c73d299b47bda2a9407eb4e WEB_BRANCH=stable-8.0 diff --git a/.github/release_template.md b/.github/release_template.md index 51312c295..6d3bc4947 100644 --- a/.github/release_template.md +++ b/.github/release_template.md @@ -4,7 +4,9 @@ ### Prerequisites * [ ] DEV/QA: Kickoff meeting [Kickoff meeting] (https://confluence.owncloud.com/display/QA/Technical+release+3.0.0+Overview) -* [ ] DEV/QA: Define client versions and provide a list of new features +* [ ] DEV/QA: Define client versions and provide list of breaking changes for desktop/mobile team +* [ ] DEV/QA: Check new strings and align with clients +* [ ] DEV/DOCS: Create list of pending docs tasks * [ ] DEV: Create branch `release-x.x.x-rc.x` -> CODEFREEZE * [ ] DEV: bump ocis version in necessary files * [ ] DEV: `changelog/CHANGELOG.tmpl` @@ -53,6 +55,12 @@ ### After QA Phase * [ ] Brief company-wide heads up via mail @tbsbdr +* [ ] Create list of changed ENV vars and send to release-coordination@owncloud.com + * [ ] Variable Name + * [ ] Introduced in version + * [ ] Default Value + * [ ] Description + * [ ] dependencies with user other components * [ ] DEV: Create branch `release-x.x.x` * [ ] DEV: bump ocis version in necessary files * [ ] DEV: `ocis-pkg/version/version.go` diff --git a/.make/check-env-var-annotations.sh b/.make/check-env-var-annotations.sh new file mode 100755 index 000000000..ffccfb8cf --- /dev/null +++ b/.make/check-env-var-annotations.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# The following grep will filter out every line containing an `env` annotation +# it will ignore every line that has allready a valid `introductionVersion` annotation +# +# valid examples: +# +# introductionVersion:"pre5.0" +# introductionVersion:"5.0" +# introductionVersion:"4.9.3-rc5" +# introductionVersion:"5.0.1-cheesecake" +# introductionVersion:"5.10.100.15" +# introductionVersion:"0.0" +# +# invalid examples: +# +# introductionVersion:"5.0cheesecake" +# introductionVersion:"5" +# introductionVersion:"5blueberry" +# introductionVersion:"5-lasagna" +# introductionVersion:"4.9.3rc5" + +ERROR=0 + +SEMVER_REGEX="([0-9]|[1-9][0-9]*)(\.([0-9]|[1-9][0-9]*)){1,2}(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?" + +QUERY_INTRO=$(git grep "env:" -- '*.go' |grep -v -P "introductionVersion:\"($SEMVER_REGEX|(pre5\.0))\""|grep -v "_test.go"|grep -v "vendor/") + +RESULTS_INTRO=$(echo "${QUERY_INTRO}"|wc -l) +if [ "${RESULTS_INTRO}" -gt 0 ]; then + echo "===============================================================================================" + echo "The following ${RESULTS_INTRO} files contain an invalid introductionVersion annotation:" + echo "===============================================================================================" + echo "$QUERY_INTRO" + ERROR=1 +fi + +# The following grep will filter out every line containing an `env` annotation +# it will ignore every line that has allready a valid `desc` annotation + +QUERY_DESC=$(git grep "env:" -- '*.go' |grep -v -P "desc:\".{10,}\""|grep -v "_test.go"|grep -v "vendor/") + +RESULTS_DESC=$(echo "${QUERY_DESC}"|wc -l) +if [ "${RESULTS_DESC}" -gt 0 ]; then + echo "===============================================================================================" + echo "The following ${RESULTS_DESC} files contain an invalid description annotation:" + echo "===============================================================================================" + echo "$QUERY_DESC" + ERROR=1 +fi +exit ${ERROR} diff --git a/CHANGELOG.md b/CHANGELOG.md index e309114f3..ae7e3b399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Table of Contents -* [Changelog for 5.0.0-rc.4](#changelog-for-500-rc4-2024-02-07) +* [Changelog for unreleased](#changelog-for-unreleased-unreleased) +* [Changelog for 5.0.0-rc.1](#changelog-for-500-rc1-2023-12-27) * [Changelog for 4.0.4](#changelog-for-404-2023-12-07) * [Changelog for 4.0.3](#changelog-for-403-2023-11-24) * [Changelog for 4.0.2](#changelog-for-402-2023-09-28) @@ -31,11 +32,411 @@ * [Changelog for 1.1.0](#changelog-for-110-2021-01-22) * [Changelog for 1.0.0](#changelog-for-100-2020-12-17) -# Changelog for [5.0.0-rc.4] (2024-02-07) +# Changelog for [unreleased] (UNRELEASED) -The following sections list the changes for 5.0.0-rc.4. +The following sections list the changes for unreleased. -[5.0.0-rc.4]: https://github.com/owncloud/ocis/compare/v4.0.4...v5.0.0-rc.4 +[unreleased]: https://github.com/owncloud/ocis/compare/v5.0.0-rc.1...master + +## Summary + +* Bugfix - Fix search response: [#7815](https://github.com/owncloud/ocis/pull/7815) +* Bugfix - The race conditions in tests: [#7847](https://github.com/owncloud/ocis/pull/7847) +* Bugfix - Fix jwt config of policies service: [#7893](https://github.com/owncloud/ocis/pull/7893) +* Bugfix - Fix Service Account Roles on external IDP: [#8074](https://github.com/owncloud/ocis/pull/8074) +* Bugfix - Fix the upload postprocessing: [#8117](https://github.com/owncloud/ocis/pull/8117) +* Bugfix - Disallow to delete a file during the processing: [#8132](https://github.com/owncloud/ocis/pull/8132) +* Bugfix - Fix wrong naming in nats-js-kv registry: [#8140](https://github.com/owncloud/ocis/pull/8140) +* Bugfix - IDP CS3 backend sessions now survive a restart: [#8142](https://github.com/owncloud/ocis/pull/8142) +* Bugfix - Fix patching of language: [#8182](https://github.com/owncloud/ocis/pull/8182) +* Bugfix - Fix search service to not log expected cases as errors: [#8200](https://github.com/owncloud/ocis/pull/8200) +* Bugfix - Updating and reset logo failed: [#8211](https://github.com/owncloud/ocis/pull/8211) +* Bugfix - Cleanup graph/pkg/service/v0/driveitems.go: [#8228](https://github.com/owncloud/ocis/pull/8228) +* Bugfix - Cleanup `search/pkg/search/search.go`: [#8230](https://github.com/owncloud/ocis/pull/8230) +* Bugfix - Graph/sharedWithMe works for shares from project spaces now: [#8233](https://github.com/owncloud/ocis/pull/8233) +* Bugfix - Fix PATCH/DELETE status code for drives that don't support them: [#8235](https://github.com/owncloud/ocis/pull/8235) +* Bugfix - Fix nats authentication: [#8236](https://github.com/owncloud/ocis/pull/8236) +* Bugfix - Fix the resource name: [#8246](https://github.com/owncloud/ocis/pull/8246) +* Bugfix - Apply role constraints when creating shares via the graph API: [#8247](https://github.com/owncloud/ocis/pull/8247) +* Bugfix - Fix concurrent access to a map: [#8269](https://github.com/owncloud/ocis/pull/8269) +* Bugfix - Fix nats registry: [#8281](https://github.com/owncloud/ocis/pull/8281) +* Bugfix - Remove invalid environment variables: [#8303](https://github.com/owncloud/ocis/pull/8303) +* Bugfix - Fix concurrent shares config: [#8317](https://github.com/owncloud/ocis/pull/8317) +* Bugfix - Fix Content-Disposition header for downloads: [#8381](https://github.com/owncloud/ocis/pull/8381) +* Bugfix - Signed url verification: [#8385](https://github.com/owncloud/ocis/pull/8385) +* Bugfix - Fix an error when move: [#8396](https://github.com/owncloud/ocis/pull/8396) +* Bugfix - Fix extended env parser: [#8409](https://github.com/owncloud/ocis/pull/8409) +* Bugfix - Graph/drives/permission Expiration date update: [#8413](https://github.com/owncloud/ocis/pull/8413) +* Bugfix - Fix search error message: [#8444](https://github.com/owncloud/ocis/pull/8444) +* Enhancement - Support login page background configuration: [#7674](https://github.com/owncloud/ocis/issues/7674) +* Enhancement - Disable the password policy: [#7985](https://github.com/owncloud/ocis/pull/7985) +* Enhancement - Update antivirus service: [#8062](https://github.com/owncloud/ocis/pull/8062) +* Enhancement - Remove deprecated environment variables: [#8149](https://github.com/owncloud/ocis/pull/8149) +* Enhancement - Disable the password policy: [#8152](https://github.com/owncloud/ocis/pull/8152) +* Enhancement - Update reva to latest edge version: [#8287](https://github.com/owncloud/ocis/pull/8287) +* Enhancement - Allow restarting multiple uploads with one command: [#8287](https://github.com/owncloud/ocis/pull/8287) +* Enhancement - Modify the concurrency default: [#8309](https://github.com/owncloud/ocis/pull/8309) +* Enhancement - Improve ocis single binary start: [#8320](https://github.com/owncloud/ocis/pull/8320) +* Enhancement - Use environment variables in yaml config files: [#8339](https://github.com/owncloud/ocis/pull/8339) +* Enhancement - Bump reva: [#8340](https://github.com/owncloud/ocis/pull/8340) +* Enhancement - Allow sending multiple user ids in one sse event: [#8379](https://github.com/owncloud/ocis/pull/8379) +* Enhancement - Allow to skip service listing: [#8408](https://github.com/owncloud/ocis/pull/8408) +* Enhancement - Add a make step to validate the env var annotations: [#8436](https://github.com/owncloud/ocis/pull/8436) + +## Details + +* Bugfix - Fix search response: [#7815](https://github.com/owncloud/ocis/pull/7815) + + We fixed the search response code from 500 to 400 when the request is invalid + + https://github.com/owncloud/ocis/issues/7812 + https://github.com/owncloud/ocis/pull/7815 + +* Bugfix - The race conditions in tests: [#7847](https://github.com/owncloud/ocis/pull/7847) + + We fixed the race conditions in tests. + + https://github.com/owncloud/ocis/issues/7846 + https://github.com/owncloud/ocis/pull/7847 + +* Bugfix - Fix jwt config of policies service: [#7893](https://github.com/owncloud/ocis/pull/7893) + + Removes jwt config of policies service + + https://github.com/owncloud/ocis/pull/7893 + +* Bugfix - Fix Service Account Roles on external IDP: [#8074](https://github.com/owncloud/ocis/pull/8074) + + Service Account Roles wouldn't work when using with external IDP + + https://github.com/owncloud/ocis/pull/8074 + +* Bugfix - Fix the upload postprocessing: [#8117](https://github.com/owncloud/ocis/pull/8117) + + We fixed the upload postprocessing when the destination file does not exist + anymore. + + https://github.com/owncloud/ocis/issues/7909 + https://github.com/owncloud/ocis/pull/8117 + +* Bugfix - Disallow to delete a file during the processing: [#8132](https://github.com/owncloud/ocis/pull/8132) + + We want to disallow deleting a file during the processing to prevent collecting + the orphan uploads. + + https://github.com/owncloud/ocis/issues/8127 + https://github.com/owncloud/ocis/pull/8132 + https://github.com/cs3org/reva/pull/4446 + +* Bugfix - Fix wrong naming in nats-js-kv registry: [#8140](https://github.com/owncloud/ocis/pull/8140) + + Registers the registry under the correct name + + https://github.com/owncloud/ocis/pull/8140 + +* Bugfix - IDP CS3 backend sessions now survive a restart: [#8142](https://github.com/owncloud/ocis/pull/8142) + + We now correctly reinitialize the CS3 backend session after the IDP service has + been restarted. + + https://github.com/owncloud/ocis/pull/8142 + +* Bugfix - Fix patching of language: [#8182](https://github.com/owncloud/ocis/pull/8182) + + User would not be able to patch their preferred language when the ldap backend + is set to `read-only`. This makes no sense as language is stored elsewhere. + + https://github.com/owncloud/ocis/pull/8182 + +* Bugfix - Fix search service to not log expected cases as errors: [#8200](https://github.com/owncloud/ocis/pull/8200) + + We changed the search service to not log cases where resources that were about + to be indexed can no longer be found. Those are expected cases, e.g. when the + file in question has already been deleted or renamed meanwhile. + + https://github.com/owncloud/ocis/pull/8200 + +* Bugfix - Updating and reset logo failed: [#8211](https://github.com/owncloud/ocis/pull/8211) + + We fixed a bug when admin tried to update or reset the logo. + + https://github.com/owncloud/ocis/issues/8101 + https://github.com/owncloud/ocis/pull/8211 + +* Bugfix - Cleanup graph/pkg/service/v0/driveitems.go: [#8228](https://github.com/owncloud/ocis/pull/8228) + + Main fix is using proto getters to avoid panics. But some other code + improvements were also done + + https://github.com/owncloud/ocis/pull/8228 + +* Bugfix - Cleanup `search/pkg/search/search.go`: [#8230](https://github.com/owncloud/ocis/pull/8230) + + Now uses proto getters to avoid panics. + + https://github.com/owncloud/ocis/pull/8230 + +* Bugfix - Graph/sharedWithMe works for shares from project spaces now: [#8233](https://github.com/owncloud/ocis/pull/8233) + + We fixed a bug in the 'graph/v1beta1/me/drive/sharedWithMe' endpoint that caused + an error response when the user received shares from project spaces. + Additionally the endpoint now behaves more graceful in cases where the + displayname of the owner or creator of a share or shared resource couldn't be + resolved. + + https://github.com/owncloud/ocis/issues/8027 + https://github.com/owncloud/ocis/issues/8215 + https://github.com/owncloud/ocis/pull/8233 + +* Bugfix - Fix PATCH/DELETE status code for drives that don't support them: [#8235](https://github.com/owncloud/ocis/pull/8235) + + Updating and Deleting the virtual drives for shares is currently not supported. + Instead of returning a generic 500 status we return a 405 response now. + + https://github.com/owncloud/ocis/issues/7881 + https://github.com/owncloud/ocis/pull/8235 + +* Bugfix - Fix nats authentication: [#8236](https://github.com/owncloud/ocis/pull/8236) + + Fixes nats authentication for registry/events/stores + + https://github.com/owncloud/ocis/pull/8236 + +* Bugfix - Fix the resource name: [#8246](https://github.com/owncloud/ocis/pull/8246) + + We fixed a problem where after renaming resource as sharer the receiver see a + new name. + + https://github.com/owncloud/ocis/issues/8242 + https://github.com/owncloud/ocis/pull/8246 + https://github.com/cs3org/reva/pull/4463 + +* Bugfix - Apply role constraints when creating shares via the graph API: [#8247](https://github.com/owncloud/ocis/pull/8247) + + We fixed a bug in the graph API for creating and updating shares so that + Spaceroot specific roles like 'Manager' and 'Co-owner' can no longer be assigned + for shares on files or directories. + + https://github.com/owncloud/ocis/issues/8131 + https://github.com/owncloud/ocis/pull/8247 + +* Bugfix - Fix concurrent access to a map: [#8269](https://github.com/owncloud/ocis/pull/8269) + + We fixed the race condition that led to concurrent map access in a publicshare + manager. + + https://github.com/owncloud/ocis/issues/8255 + https://github.com/owncloud/ocis/pull/8269 + https://github.com/cs3org/reva/pull/4472 + +* Bugfix - Fix nats registry: [#8281](https://github.com/owncloud/ocis/pull/8281) + + The nats registry would behave badly when configuring `nats-js-kv` via envvar. + Reason is the way go-micro initializes. It took 5 developers to find the issue + and the fix so the details cannot be shared here. Just accept that it is working + now + + https://github.com/owncloud/ocis/pull/8281 + +* Bugfix - Remove invalid environment variables: [#8303](https://github.com/owncloud/ocis/pull/8303) + + We have removed two spaces related environment variables (whether project spaces + and the share jail are enabled) and hardcoded the only allowed options. Misusing + those variables would have resulted in invalid config. + + https://github.com/owncloud/ocis/pull/8303 + +* Bugfix - Fix concurrent shares config: [#8317](https://github.com/owncloud/ocis/pull/8317) + + We fixed setting the config for concurrent web requests, which did not work as + expected before. + + https://github.com/owncloud/ocis/pull/8317 + +* Bugfix - Fix Content-Disposition header for downloads: [#8381](https://github.com/owncloud/ocis/pull/8381) + + We have fixed a bug that caused downloads to fail on Chromebased browsers when + the filename contained special characters. + + https://github.com/owncloud/ocis/issues/8361 + https://github.com/owncloud/ocis/pull/8381 + https://github.com/cs3org/reva/pull/4498 + +* Bugfix - Signed url verification: [#8385](https://github.com/owncloud/ocis/pull/8385) + + Signed urls now expire properly + + https://github.com/owncloud/ocis/pull/8385 + +* Bugfix - Fix an error when move: [#8396](https://github.com/owncloud/ocis/pull/8396) + + We fixed a bug that caused Internal Server Error when move using destination id + + https://github.com/owncloud/ocis/issues/6739 + https://github.com/owncloud/ocis/pull/8396 + https://github.com/cs3org/reva/pull/4503 + +* Bugfix - Fix extended env parser: [#8409](https://github.com/owncloud/ocis/pull/8409) + + The extended envvar parser would be angry if there are two `os.Getenv` in the + same line. We fixed this. + + https://github.com/owncloud/ocis/pull/8409 + +* Bugfix - Graph/drives/permission Expiration date update: [#8413](https://github.com/owncloud/ocis/pull/8413) + + We fixed a bug in the Update sharing permission the expiration dates can't be + removed from link permissions. + + https://github.com/owncloud/ocis/issues/8405 + https://github.com/owncloud/ocis/pull/8413 + +* Bugfix - Fix search error message: [#8444](https://github.com/owncloud/ocis/pull/8444) + + We fixed an error message returned when the search request is invalid + + https://github.com/owncloud/ocis/issues/8442 + https://github.com/owncloud/ocis/pull/8444 + +* Enhancement - Support login page background configuration: [#7674](https://github.com/owncloud/ocis/issues/7674) + + Introduce a new environment variable `IDP_LOGIN_BACKGROUND_URL` that overrides + the default background image of the IDP login page when present. + + https://github.com/owncloud/ocis/issues/7674 + https://github.com/owncloud/ocis/pull/7900 + +* Enhancement - Disable the password policy: [#7985](https://github.com/owncloud/ocis/pull/7985) + + We add the environment variable that allow to disable the password policy. + + https://github.com/owncloud/ocis/issues/7916 + https://github.com/owncloud/ocis/pull/7985 + https://github.com/cs3org/reva/pull/4409 + +* Enhancement - Update antivirus service: [#8062](https://github.com/owncloud/ocis/pull/8062) + + We update the antivirus icap client library and optimize the antivirus scanning + service. ANTIVIRUS_ICAP_TIMEOUT is now deprecated and + ANTIVIRUS_ICAP_SCAN_TIMEOUT should be used instead. + + ANTIVIRUS_ICAP_SCAN_TIMEOUT supports human durations like `1s`, `1m`, `1h` and + `1d`. + + https://github.com/owncloud/ocis/issues/6764 + https://github.com/owncloud/ocis/pull/8062 + +* Enhancement - Remove deprecated environment variables: [#8149](https://github.com/owncloud/ocis/pull/8149) + + We have removed all deprecated environment variables that have been marked for + removal for 5.0.0 + + https://github.com/owncloud/ocis/issues/8025 + https://github.com/owncloud/ocis/pull/8149 + +* Enhancement - Disable the password policy: [#8152](https://github.com/owncloud/ocis/pull/8152) + + We reworked and moved disabling the password policy logic from the reva to the + ocis. + + https://github.com/owncloud/ocis/issues/7916 + https://github.com/owncloud/ocis/pull/8152 + https://github.com/cs3org/reva/pull/4453 + +* Enhancement - Update reva to latest edge version: [#8287](https://github.com/owncloud/ocis/pull/8287) + + We update reva to the latest edge version to get the latest fixes and features. + + https://github.com/owncloud/ocis/pull/8287 + https://github.com/owncloud/ocis/pull/8278 + https://github.com/owncloud/ocis/pull/8264 + https://github.com/owncloud/ocis/pull/8100 + +* Enhancement - Allow restarting multiple uploads with one command: [#8287](https://github.com/owncloud/ocis/pull/8287) + + Allows to restart all commands in a specific state. + + https://github.com/owncloud/ocis/pull/8287 + +* Enhancement - Modify the concurrency default: [#8309](https://github.com/owncloud/ocis/pull/8309) + + We have changed the default MaxConcurrency value from 100 to 5 to prevent too + frequent gc runs on low memory systems. We have also bumped reva to pull in the + related changes from there. + + https://github.com/owncloud/ocis/issues/8257 + https://github.com/owncloud/ocis/pull/8309 + https://github.com/cs3org/reva/pull/4485 + +* Enhancement - Improve ocis single binary start: [#8320](https://github.com/owncloud/ocis/pull/8320) + + Removes waiting times when starting the single binary. Improves ocis single + binary boot time from 8s to 2.5s + + https://github.com/owncloud/ocis/pull/8320 + +* Enhancement - Use environment variables in yaml config files: [#8339](https://github.com/owncloud/ocis/pull/8339) + + We added the ability to use environment variables in yaml config files. This + allows to use environment variables in the config files of the ocis services + which will be replaced by the actual value of the environment variable at + runtime. + + Example: + + ``` + web: + http: + addr: ${SOME_HTTP_ADDR} + ``` + + This makes it possible to use the same config file for different environments + without the need to change the config file itself. This is especially useful + when using docker-compose to run the ocis services. It is a common pattern to + create an .env file which contains the environment variables for the + docker-compose file. Now you can use the same .env file to configure the ocis + services. + + https://github.com/owncloud/ocis/pull/8339 + +* Enhancement - Bump reva: [#8340](https://github.com/owncloud/ocis/pull/8340) + + We have bumped reve to pull in the changes needed for automatically increment + filenames on upload collisions in secret filedrops. + + https://github.com/owncloud/ocis/issues/8291 + https://github.com/owncloud/ocis/pull/8340 + +* Enhancement - Allow sending multiple user ids in one sse event: [#8379](https://github.com/owncloud/ocis/pull/8379) + + Sending multiple user ids in one sse event is now possible which reduces the + number of sent events. + + https://github.com/owncloud/ocis/pull/8379 + https://github.com/cs3org/reva/pull/4501 + +* Enhancement - Allow to skip service listing: [#8408](https://github.com/owncloud/ocis/pull/8408) + + The ocis version cmd listed all services by default. This is not always + intended, so we allow to skip the listing of the services by using the + --skip-services flag. + + https://github.com/owncloud/ocis/issues/8070 + https://github.com/owncloud/ocis/pull/8408 + +* Enhancement - Add a make step to validate the env var annotations: [#8436](https://github.com/owncloud/ocis/pull/8436) + + We have added a make step `make validate-env-var-annotations` to validate the + env var annotations in to the environment variables. + + https://github.com/owncloud/ocis/issues/8258 + https://github.com/owncloud/ocis/pull/8436 + +# Changelog for [5.0.0-rc.1] (2023-12-27) + +The following sections list the changes for 5.0.0-rc.1. + +[5.0.0-rc.1]: https://github.com/owncloud/ocis/compare/v4.0.4...v5.0.0-rc.1 ## Summary @@ -62,10 +463,8 @@ The following sections list the changes for 5.0.0-rc.4. * Bugfix - Fix the tgz mime type: [#7772](https://github.com/owncloud/ocis/pull/7772) * Bugfix - Fix natsjs cache: [#7790](https://github.com/owncloud/ocis/pull/7790) * Bugfix - Fix search service start: [#7795](https://github.com/owncloud/ocis/pull/7795) -* Bugfix - The race conditions in tests: [#7847](https://github.com/owncloud/ocis/pull/7847) * Bugfix - Do not purge expired upload sessions that are still postprocessing: [#7859](https://github.com/owncloud/ocis/pull/7859) * Bugfix - Fix the public link update: [#7862](https://github.com/owncloud/ocis/pull/7862) -* Bugfix - Fix jwt config of policies service: [#7893](https://github.com/owncloud/ocis/pull/7893) * Bugfix - Updating logo with new theme structure: [#7930](https://github.com/owncloud/ocis/pull/7930) * Bugfix - Password policy return code was wrong: [#7952](https://github.com/owncloud/ocis/pull/7952) * Bugfix - Removed outdated and unused dependency from idp package: [#7957](https://github.com/owncloud/ocis/issues/7957) @@ -76,25 +475,6 @@ The following sections list the changes for 5.0.0-rc.4. * Bugfix - Non durable streams for sse service: [#7986](https://github.com/owncloud/ocis/pull/7986) * Bugfix - Fix empty trace ids: [#8023](https://github.com/owncloud/ocis/pull/8023) * Bugfix - Fix search by containing special characters: [#8050](https://github.com/owncloud/ocis/pull/8050) -* Bugfix - Fix the upload postprocessing: [#8117](https://github.com/owncloud/ocis/pull/8117) -* Bugfix - Disallow to delete a file during the processing: [#8132](https://github.com/owncloud/ocis/pull/8132) -* Bugfix - Fix wrong naming in nats-js-kv registry: [#8140](https://github.com/owncloud/ocis/pull/8140) -* Bugfix - IDP CS3 backend sessions now survire restart: [#8142](https://github.com/owncloud/ocis/pull/8142) -* Bugfix - Fix patching of language: [#8182](https://github.com/owncloud/ocis/pull/8182) -* Bugfix - Fix search service to not log expected cases as errors: [#8200](https://github.com/owncloud/ocis/pull/8200) -* Bugfix - Updating and reset logo failed: [#8211](https://github.com/owncloud/ocis/pull/8211) -* Bugfix - Cleanup graph/pkg/service/v0/driveitems.go: [#8228](https://github.com/owncloud/ocis/pull/8228) -* Bugfix - Cleanup `search/pkg/search/search.go`: [#8230](https://github.com/owncloud/ocis/pull/8230) -* Bugfix - Graph/sharedWithMe works for shares from project spaces now: [#8233](https://github.com/owncloud/ocis/pull/8233) -* Bugfix - Fix PATCH/DELETE status code for drives that don't support them: [#8235](https://github.com/owncloud/ocis/pull/8235) -* Bugfix - Fix nats authentication: [#8236](https://github.com/owncloud/ocis/pull/8236) -* Bugfix - Fix the resource name: [#8246](https://github.com/owncloud/ocis/pull/8246) -* Bugfix - Apply role constraints when creating shares via the graph API: [#8247](https://github.com/owncloud/ocis/pull/8247) -* Bugfix - Fix concurrent access to a map: [#8269](https://github.com/owncloud/ocis/pull/8269) -* Bugfix - Fix nats registry: [#8281](https://github.com/owncloud/ocis/pull/8281) -* Bugfix - Remove invalid environment variables: [#8303](https://github.com/owncloud/ocis/pull/8303) -* Bugfix - Fix concurrent shares config: [#8317](https://github.com/owncloud/ocis/pull/8317) -* Bugfix - Signed url verification: [#8385](https://github.com/owncloud/ocis/pull/8385) * Bugfix - Fix last month search: [#31145](https://github.com/golang/go/issues/31145) * Change - Auto-Accept Shares: [#7097](https://github.com/owncloud/ocis/pull/7097) * Change - Change the default TUS chunk size: [#7273](https://github.com/owncloud/ocis/pull/7273) @@ -103,12 +483,12 @@ The following sections list the changes for 5.0.0-rc.4. * Enhancement - Add the Banned Passwords List: [#4197](https://github.com/cs3org/reva/pull/4197) * Enhancement - Introduce service accounts: [#6427](https://github.com/owncloud/ocis/pull/6427) * Enhancement - SSE for messaging: [#6992](https://github.com/owncloud/ocis/pull/6992) -* Enhancement - Support spec violating AD FS access token issuer: [#7138](https://github.com/owncloud/ocis/pull/7138) +* Enhancement - Support spec violating AD FS access token issuer: [#7140](https://github.com/owncloud/ocis/pull/7140) * Enhancement - Add OCIS_LDAP_BIND_PASSWORD as replacement for LDAP_BIND_PASSWORD: [#7176](https://github.com/owncloud/ocis/issues/7176) * Enhancement - Keyword Query Language (KQL) search syntax: [#7212](https://github.com/owncloud/ocis/pull/7212) * Enhancement - Introduce clientlog service: [#7217](https://github.com/owncloud/ocis/pull/7217) * Enhancement - Proxy uses service accounts for provisioning: [#7240](https://github.com/owncloud/ocis/pull/7240) -* Enhancement - The password policies change request: [#7264](https://github.com/cs3org/reva/pull/7264) +* Enhancement - The password policies change request: [#7264](https://github.com/owncloud/ocis/pull/7264) * Enhancement - Introduce natsjs registry: [#7272](https://github.com/owncloud/ocis/issues/7272) * Enhancement - Add the password policies: [#7285](https://github.com/owncloud/ocis/pull/7285) * Enhancement - Add login URL config: [#7317](https://github.com/owncloud/ocis/pull/7317) @@ -131,11 +511,10 @@ The following sections list the changes for 5.0.0-rc.4. * Enhancement - Add search MediaType filter: [#7602](https://github.com/owncloud/ocis/pull/7602) * Enhancement - Add Sharing NG endpoints: [#7633](https://github.com/owncloud/ocis/pull/7633) * Enhancement - Configs for Web embed mode: [#7670](https://github.com/owncloud/ocis/pull/7670) -* Enhancement - Support login page background configuration: [#7674](https://github.com/owncloud/ocis/issues/7674) * Enhancement - Add new permissions: [#7700](https://github.com/owncloud/ocis/pull/7700) * Enhancement - Add preferred language to user settings: [#7720](https://github.com/owncloud/ocis/pull/7720) * Enhancement - Add user filter startswith and contains: [#7739](https://github.com/owncloud/ocis/pull/7739) -* Enhancement - Allow configuring additional routes: [#7740](https://github.com/owncloud/ocis/pull/7740) +* Enhancement - Allow configuring additional routes: [#7741](https://github.com/owncloud/ocis/pull/7741) * Enhancement - Default link permission config: [#7783](https://github.com/owncloud/ocis/pull/7783) * Enhancement - Add banned password list to the default deployments: [#7784](https://github.com/owncloud/ocis/pull/7784) * Enhancement - Update to go 1.21: [#7794](https://github.com/owncloud/ocis/pull/7794) @@ -149,19 +528,13 @@ The following sections list the changes for 5.0.0-rc.4. * Enhancement - Add cli commands for trash-bin: [#7917](https://github.com/owncloud/ocis/pull/7917) * Enhancement - Add validation update public share: [#7978](https://github.com/owncloud/ocis/pull/7978) * Enhancement - Allow inmemory nats-js-kv stores: [#7979](https://github.com/owncloud/ocis/pull/7979) -* Enhancement - Disable the password policy: [#7985](https://github.com/owncloud/ocis/pull/7985) * Enhancement - Use kv store in natsjs registry: [#7987](https://github.com/owncloud/ocis/pull/7987) * Enhancement - Allow authentication nats connections: [#7989](https://github.com/owncloud/ocis/pull/7989) * Enhancement - Add RED metrics to the metrics endpoint: [#7994](https://github.com/owncloud/ocis/pull/7994) * Enhancement - Add ocm and sciencemesh services: [#7998](https://github.com/owncloud/ocis/pull/7998) * Enhancement - Make nats-js-kv the default registry: [#8011](https://github.com/owncloud/ocis/pull/8011) +* Enhancement - Update Reva to version 2.18.0: [#8038](https://github.com/owncloud/ocis/pull/8038) * Enhancement - Service Account roles: [#8051](https://github.com/owncloud/ocis/pull/8051) -* Enhancement - Update antivirus service: [#8062](https://github.com/owncloud/ocis/pull/8062) -* Enhancement - Remove deprecated environment variables: [#8149](https://github.com/owncloud/ocis/pull/8149) -* Enhancement - Disable the password policy: [#8152](https://github.com/owncloud/ocis/pull/8152) -* Enhancement - Update reva to latest edge version: [#8287](https://github.com/owncloud/ocis/pull/8287) -* Enhancement - Allow restarting multiple uploads with one command: [#8287](https://github.com/owncloud/ocis/pull/8287) -* Enhancement - Modify the concurrency default: [#8309](https://github.com/owncloud/ocis/pull/8309) * Enhancement - Update web to v8.0.0-rc.3: [#8342](https://github.com/owncloud/ocis/pull/8342) ## Details @@ -350,13 +723,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/7795 -* Bugfix - The race conditions in tests: [#7847](https://github.com/owncloud/ocis/pull/7847) - - We fixed the race conditions in tests. - - https://github.com/owncloud/ocis/issues/7846 - https://github.com/owncloud/ocis/pull/7847 - * Bugfix - Do not purge expired upload sessions that are still postprocessing: [#7859](https://github.com/owncloud/ocis/pull/7859) https://github.com/owncloud/ocis/pull/7859 @@ -370,12 +736,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/issues/7821 https://github.com/owncloud/ocis/pull/7862 -* Bugfix - Fix jwt config of policies service: [#7893](https://github.com/owncloud/ocis/pull/7893) - - Removes jwt config of policies service - - https://github.com/owncloud/ocis/pull/7893 - * Bugfix - Updating logo with new theme structure: [#7930](https://github.com/owncloud/ocis/pull/7930) Updating and resetting the logo when using the new `theme.json` structure in Web @@ -449,154 +809,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/8050 https://github.com/owncloud/ocis/pull/8035 -* Bugfix - Fix the upload postprocessing: [#8117](https://github.com/owncloud/ocis/pull/8117) - - We fixed the upload postprocessing when the destination file does not exist - anymore. - - https://github.com/owncloud/ocis/issues/7909 - https://github.com/owncloud/ocis/pull/8117 - -* Bugfix - Disallow to delete a file during the processing: [#8132](https://github.com/owncloud/ocis/pull/8132) - - We want to disallow deleting a file during the processing to prevent collecting - the orphan uploads. - - https://github.com/owncloud/ocis/issues/8127 - https://github.com/owncloud/ocis/pull/8132 - https://github.com/cs3org/reva/pull/4446 - -* Bugfix - Fix wrong naming in nats-js-kv registry: [#8140](https://github.com/owncloud/ocis/pull/8140) - - Registers the registry under the correct name - - https://github.com/owncloud/ocis/pull/8140 - -* Bugfix - IDP CS3 backend sessions now survire restart: [#8142](https://github.com/owncloud/ocis/pull/8142) - - We now correctly reinitialize the CS3 backend session after the IDP has been - restarted. - - https://github.com/owncloud/ocis/pull/8142 - -* Bugfix - Fix patching of language: [#8182](https://github.com/owncloud/ocis/pull/8182) - - User would not be able to patch their preferred language when the ldap backend - is set to `read-only`. This makes no sense as language is stored elsewhere. - - https://github.com/owncloud/ocis/pull/8182 - -* Bugfix - Fix search service to not log expected cases as errors: [#8200](https://github.com/owncloud/ocis/pull/8200) - - We changed the search service to not log cases where resources that were about - to be indexed can no longer be found. Those are expected cases, e.g. when the - file in question has already been deleted or renamed meanwhile. - - https://github.com/owncloud/ocis/pull/8200 - -* Bugfix - Updating and reset logo failed: [#8211](https://github.com/owncloud/ocis/pull/8211) - - We fixed a bug when admin tried to update or reset the logo. - - https://github.com/owncloud/ocis/issues/8101 - https://github.com/owncloud/ocis/pull/8211 - -* Bugfix - Cleanup graph/pkg/service/v0/driveitems.go: [#8228](https://github.com/owncloud/ocis/pull/8228) - - Main fix is using proto getters to avoid panics. But some other code - improvements were also done - - https://github.com/owncloud/ocis/pull/8228 - -* Bugfix - Cleanup `search/pkg/search/search.go`: [#8230](https://github.com/owncloud/ocis/pull/8230) - - Now uses proto getters to avoid panics. - - https://github.com/owncloud/ocis/pull/8230 - -* Bugfix - Graph/sharedWithMe works for shares from project spaces now: [#8233](https://github.com/owncloud/ocis/pull/8233) - - We fixed a bug in the 'graph/v1beta1/me/drive/sharedWithMe' endpoint that caused - an error response when the user received shares from project spaces. - Additionally the endpoint now behaves more graceful in cases where the - displayname of the owner or creator of a share or shared resource couldn't be - resolved. - - https://github.com/owncloud/ocis/issues/8027 - https://github.com/owncloud/ocis/issues/8215 - https://github.com/owncloud/ocis/pull/8233 - -* Bugfix - Fix PATCH/DELETE status code for drives that don't support them: [#8235](https://github.com/owncloud/ocis/pull/8235) - - Updating and Deleting the virtual drives for shares is currently not supported. - Instead of returning a generic 500 status we return a 405 response now. - - https://github.com/owncloud/ocis/issues/7881 - https://github.com/owncloud/ocis/pull/8235 - -* Bugfix - Fix nats authentication: [#8236](https://github.com/owncloud/ocis/pull/8236) - - Fixes nats authentication for registry/events/stores - - https://github.com/owncloud/ocis/pull/8236 - -* Bugfix - Fix the resource name: [#8246](https://github.com/owncloud/ocis/pull/8246) - - We fixed a problem where after renaming resource as sharer the receiver see a - new name. - - https://github.com/owncloud/ocis/issues/8242 - https://github.com/owncloud/ocis/pull/8246 - https://github.com/cs3org/reva/pull/4463 - -* Bugfix - Apply role constraints when creating shares via the graph API: [#8247](https://github.com/owncloud/ocis/pull/8247) - - We fixed a bug in the graph API for creating and updating shares so that - Spaceroot specific roles like 'Manager' and 'Co-owner' can no longer be assigned - for shares on files or directories. - - https://github.com/owncloud/ocis/issues/8131 - https://github.com/owncloud/ocis/pull/8247 - -* Bugfix - Fix concurrent access to a map: [#8269](https://github.com/owncloud/ocis/pull/8269) - - We fixed the race condition that led to concurrent map access in a publicshare - manager. - - https://github.com/owncloud/ocis/issues/8255 - https://github.com/owncloud/ocis/pull/8269 - https://github.com/cs3org/reva/pull/4472 - -* Bugfix - Fix nats registry: [#8281](https://github.com/owncloud/ocis/pull/8281) - - The nats registry would behave badly when configuring `nats-js-kv` via envvar. - Reason is the way go-micro initializes. It took 5 developers to find the issue - and the fix so the details cannot be shared here. Just accept that it is working - now - - https://github.com/owncloud/ocis/pull/8281 - -* Bugfix - Remove invalid environment variables: [#8303](https://github.com/owncloud/ocis/pull/8303) - - We have removed two spaces related environment variables (whether project spaces - and the share jail are enabled) and hardcoded the only allowed options. Misusing - those variables would have resulted in invalid config. - - https://github.com/owncloud/ocis/pull/8303 - -* Bugfix - Fix concurrent shares config: [#8317](https://github.com/owncloud/ocis/pull/8317) - - We fixed setting the config for concurrent web requests, which did not work as - expected before. - - https://github.com/owncloud/ocis/pull/8317 - -* Bugfix - Signed url verification: [#8385](https://github.com/owncloud/ocis/pull/8385) - - Signed urls now expire properly - - https://github.com/owncloud/ocis/pull/8385 - * Bugfix - Fix last month search: [#31145](https://github.com/golang/go/issues/31145) We've fixed the last month search edge case when currently is 31-th. @@ -669,17 +881,16 @@ The following sections list the changes for 5.0.0-rc.4. * notify postprocessing state changes. * notify file locking and unlocking. - * ... @toDo https://github.com/owncloud/ocis/pull/6992 -* Enhancement - Support spec violating AD FS access token issuer: [#7138](https://github.com/owncloud/ocis/pull/7138) +* Enhancement - Support spec violating AD FS access token issuer: [#7140](https://github.com/owncloud/ocis/pull/7140) AD FS `/adfs/.well-known/openid-configuration` has an optional `access_token_issuer` which, in violation of the OpenID Connect spec, takes precedence over `issuer`. - https://github.com/owncloud/ocis/pull/7138 + https://github.com/owncloud/ocis/pull/7140 * Enhancement - Add OCIS_LDAP_BIND_PASSWORD as replacement for LDAP_BIND_PASSWORD: [#7176](https://github.com/owncloud/ocis/issues/7176) @@ -760,11 +971,11 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/issues/5550 https://github.com/owncloud/ocis/pull/7240 -* Enhancement - The password policies change request: [#7264](https://github.com/cs3org/reva/pull/7264) +* Enhancement - The password policies change request: [#7264](https://github.com/owncloud/ocis/pull/7264) The variables renaming OCIS-3767 - https://github.com/cs3org/reva/pull/7264 + https://github.com/owncloud/ocis/pull/7264 * Enhancement - Introduce natsjs registry: [#7272](https://github.com/owncloud/ocis/issues/7272) @@ -1040,14 +1251,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/web/issues/9768 https://github.com/owncloud/ocis/pull/7670 -* Enhancement - Support login page background configuration: [#7674](https://github.com/owncloud/ocis/issues/7674) - - Introduce a new environment variable `IDP_LOGIN_BACKGROUND_URL` that overrides - the default background image of the IDP login page when present. - - https://github.com/owncloud/ocis/issues/7674 - https://github.com/owncloud/ocis/pull/7900 - * Enhancement - Add new permissions: [#7700](https://github.com/owncloud/ocis/pull/7700) Adds new permissions to admin/spaceadmin/user roles - Favorites.List allows / @@ -1076,12 +1279,12 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/issues/5486 https://github.com/owncloud/ocis/pull/7739 -* Enhancement - Allow configuring additional routes: [#7740](https://github.com/owncloud/ocis/pull/7740) +* Enhancement - Allow configuring additional routes: [#7741](https://github.com/owncloud/ocis/pull/7741) - Allows adding custom routes to the ocis proxy. This enables customs ocis - extensions + Allows adding custom routes to the ocis proxy. This enables custom ocis + extensions. - https://github.com/owncloud/ocis/pull/7740 + https://github.com/owncloud/ocis/pull/7741 * Enhancement - Default link permission config: [#7783](https://github.com/owncloud/ocis/pull/7783) @@ -1184,14 +1387,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/7979 -* Enhancement - Disable the password policy: [#7985](https://github.com/owncloud/ocis/pull/7985) - - We add the environment variable that allow to disable the password policy. - - https://github.com/owncloud/ocis/issues/7916 - https://github.com/owncloud/ocis/pull/7985 - https://github.com/cs3org/reva/pull/4409 - * Enhancement - Use kv store in natsjs registry: [#7987](https://github.com/owncloud/ocis/pull/7987) Replaces the nats object store with the nats kv store in the natsjs registry @@ -1240,72 +1435,7 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/8011 https://github.com/owncloud/ocis/pull/8027 -* Enhancement - Service Account roles: [#8051](https://github.com/owncloud/ocis/pull/8051) - - Use a hidden role for service accounts. It will not appear in ListRoles calls - but internally handled by settings service - - https://github.com/owncloud/ocis/pull/8051 - https://github.com/owncloud/ocis/pull/8074 - -* Enhancement - Update antivirus service: [#8062](https://github.com/owncloud/ocis/pull/8062) - - We update the antivirus icap client library and optimize the antivirus scanning - service. ANTIVIRUS_ICAP_TIMEOUT is now deprecated and - ANTIVIRUS_ICAP_SCAN_TIMEOUT should be used instead. - - ANTIVIRUS_ICAP_SCAN_TIMEOUT supports human durations like `1s`, `1m`, `1h` and - `1d`. - - https://github.com/owncloud/ocis/issues/6764 - https://github.com/owncloud/ocis/pull/8062 - -* Enhancement - Remove deprecated environment variables: [#8149](https://github.com/owncloud/ocis/pull/8149) - - We have removed all deprecated environment variables that have been marked for - removal for 5.0.0 - - https://github.com/owncloud/ocis/issues/8025 - https://github.com/owncloud/ocis/pull/8149 - -* Enhancement - Disable the password policy: [#8152](https://github.com/owncloud/ocis/pull/8152) - - We reworked and moved disabling the password policy logic from the reva to the - ocis. - - https://github.com/owncloud/ocis/issues/7916 - https://github.com/owncloud/ocis/pull/8152 - https://github.com/cs3org/reva/pull/4453 - -* Enhancement - Update reva to latest edge version: [#8287](https://github.com/owncloud/ocis/pull/8287) - - We update reva to the latest edge version to get the latest fixes and features. - - ## Changelog for reva edge - - * Bugfix [cs3org/reva#4454](https://github.com/cs3org/reva/pull/4454): Skip unnecessary share retrieval - * Bugfix [cs3org/reva#4461](https://github.com/cs3org/reva/pull/4461): CORS handling for WebDAV requests fixed - * Bugfix [cs3org/reva#4462](https://github.com/cs3org/reva/pull/4462): Prevent setting container specific permissions on files - * Bugfix [cs3org/reva#4463](https://github.com/cs3org/reva/pull/4463): Fix the resource name - * Bugfix [cs3org/reva#4466](https://github.com/cs3org/reva/pull/4466): Fix natsjskv store - * Bugfix [cs3org/reva#4469](https://github.com/cs3org/reva/pull/4469): Handle interrupted uploads - * Bugfix [cs3org/reva#4472](https://github.com/cs3org/reva/pull/4472): Fix concurrent access to a map - * Bugfix [cs3org/reva#4473](https://github.com/cs3org/reva/pull/4473): Decomposedfs now correctly lists sessions - * Bugfix [cs3org/reva#4474](https://github.com/cs3org/reva/pull/4474): Make /dav/meta consistent - * Bugfix [cs3org/reva#4475](https://github.com/cs3org/reva/pull/4475): Upload session specific processing flag - * Bugfix [cs3org/reva#4476](https://github.com/cs3org/reva/pull/4476): No need to unmark postprocessing when it was not started - * Bugfix [cs3org/reva#4479](https://github.com/cs3org/reva/pull/4479): Fix creating documents in the approvider - * Bugfix [cs3org/reva#4481](https://github.com/cs3org/reva/pull/4481): Distinguish failure and node metadata reversal - * Bugfix [cs3org/reva#4434](https://github.com/cs3org/reva/pull/4434): Fix the upload postprocessing - * Bugfix [cs3org/reva#4443](https://github.com/cs3org/reva/pull/4443): Fixed wrong status code when moving a file to a denied path - * Bugfix [cs3org/reva#4446](https://github.com/cs3org/reva/pull/4446): Disallow to delete a file during the processing - * Bugfix [cs3org/reva#4448](https://github.com/cs3org/reva/pull/4448): Fix truncating existing files - * Bugfix [cs3org/reva#4449](https://github.com/cs3org/reva/pull/4449): Keep failed processing status - * Bugfix [cs3org/reva#4452](https://github.com/cs3org/reva/pull/4452): More efficient share jail - * Bugfix [cs3org/reva#4456](https://github.com/cs3org/reva/pull/4456): Do not lose revisions when restoring the first revision - * Bugfix [cs3org/reva#4457](https://github.com/cs3org/reva/pull/4457): Fix concurrent map access in sharecache - * Enhancement [cs3org/reva#4453](https://github.com/cs3org/reva/pull/4453): Disable the password policy - * Enhancement [cs3org/reva#4477](https://github.com/cs3org/reva/pull/4477): Extend ResumePostprocessing event +* Enhancement - Update Reva to version 2.18.0: [#8038](https://github.com/owncloud/ocis/pull/8038) ## Changelog for reva 2.18.0 (2023-12-22) @@ -1400,11 +1530,6 @@ The following sections list the changes for 5.0.0-rc.4. * Enhancement [cs3org/reva#4170](https://github.com/cs3org/reva/pull/4170): Update password policies * Enhancement [cs3org/reva#4232](https://github.com/cs3org/reva/pull/4232): Improve error handling in utils package - https://github.com/owncloud/ocis/pull/8287 - https://github.com/owncloud/ocis/pull/8278 - https://github.com/owncloud/ocis/pull/8264 - https://github.com/owncloud/ocis/pull/8100 - https://github.com/owncloud/ocis/pull/8100 https://github.com/owncloud/ocis/pull/8038 https://github.com/owncloud/ocis/pull/8056 https://github.com/owncloud/ocis/pull/7949 @@ -1417,21 +1542,12 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/7727 https://github.com/owncloud/ocis/pull/7752 -* Enhancement - Allow restarting multiple uploads with one command: [#8287](https://github.com/owncloud/ocis/pull/8287) +* Enhancement - Service Account roles: [#8051](https://github.com/owncloud/ocis/pull/8051) - Allows to restart all commands in a specific state. + Use a hidden role for service accounts. It will not appear in ListRoles calls + but internally handled by settings service - https://github.com/owncloud/ocis/pull/8287 - -* Enhancement - Modify the concurrency default: [#8309](https://github.com/owncloud/ocis/pull/8309) - - We have changed the default MaxConcurrency value from 100 to 5 to prevent too - frequent gc runs on low memory systems. We have also bumped reva to pull in the - related changes from there. - - https://github.com/owncloud/ocis/issues/8257 - https://github.com/owncloud/ocis/pull/8309 - https://github.com/cs3org/reva/pull/4485 + https://github.com/owncloud/ocis/pull/8051 * Enhancement - Update web to v8.0.0-rc.3: [#8342](https://github.com/owncloud/ocis/pull/8342) @@ -1681,7 +1797,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/8342 https://github.com/owncloud/ocis/pull/8154 - https://github.com/owncloud/ocis/pull/8154 https://github.com/owncloud/ocis/pull/8055 https://github.com/owncloud/ocis/pull/7930 https://github.com/owncloud/ocis/pull/7952 @@ -1689,7 +1804,6 @@ The following sections list the changes for 5.0.0-rc.4. https://github.com/owncloud/ocis/pull/7883 https://github.com/owncloud/web/releases/tag/v8.0.0-rc.3 https://github.com/owncloud/web/releases/tag/v8.0.0-rc.2 - https://github.com/owncloud/web/releases/tag/v8.0.0-rc.2 https://github.com/owncloud/web/releases/tag/v8.0.0-rc.1 https://github.com/owncloud/web/releases/tag/v8.0.0-beta.1 https://github.com/owncloud/web/releases/tag/v8.0.0-beta.2 diff --git a/Makefile b/Makefile index 2e3550266..c042d3594 100644 --- a/Makefile +++ b/Makefile @@ -168,10 +168,17 @@ clean: docs-generate: # empty the folders first to only have files that are generated without remnants find docs/services/_includes/ -type f \( -name "*" ! -name ".git*" ! -name "_*" \) -delete || exit 1 + @for mod in $(OCIS_MODULES); do \ $(MAKE) --no-print-directory -C $$mod docs-generate || exit 1; \ done + $(MAKE) --no-print-directory -C docs docs-generate || exit 1 + cp docs/services/general-info/env-var-deltas/*.adoc docs/services/_includes/adoc/env-var-deltas/ + +.PHONY: check-env-var-annotations +check-env-var-annotations: + .make/check-env-var-annotations.sh .PHONY: ci-go-generate ci-go-generate: diff --git a/changelog/4.0.5_2023-12-21/add-trach-bin-cli.md b/changelog/4.0.5_2023-12-21/add-trach-bin-cli.md new file mode 100644 index 000000000..c2c26aaa2 --- /dev/null +++ b/changelog/4.0.5_2023-12-21/add-trach-bin-cli.md @@ -0,0 +1,6 @@ +Enhancement: Add cli commands for trash-bin + +We added the `list` and `restore` commands to the trash-bin items to the CLI + +https://github.com/owncloud/ocis/pull/7936 +https://github.com/owncloud/ocis/issues/7845 diff --git a/changelog/4.0.5_2023-12-21/fix-auth-allow-empty-cred-chain.md b/changelog/4.0.5_2023-12-21/fix-auth-allow-empty-cred-chain.md new file mode 100644 index 000000000..d1eff1fe9 --- /dev/null +++ b/changelog/4.0.5_2023-12-21/fix-auth-allow-empty-cred-chain.md @@ -0,0 +1,11 @@ +Bugfix: fix reva config of frontend service to avoid misleading error logs + +We set an empty Credentials chain for the frontend service now. In ocis all +non-reva token authentication is handled by the proxy. This avoids irritating +error messages about the missing 'auth-bearer' service. + +https://github.com/owncloud/ocis/pull/7934 +https://github.com/owncloud/ocis/pull/7453 +https://github.com/cs3org/reva/pull/4396 +https://github.com/cs3org/reva/pull/4241 +https://github.com/owncloud/ocis/issues/6692 diff --git a/changelog/4.0.5_2023-12-21/fix-traces.md b/changelog/4.0.5_2023-12-21/fix-traces.md new file mode 100644 index 000000000..acdb4d527 --- /dev/null +++ b/changelog/4.0.5_2023-12-21/fix-traces.md @@ -0,0 +1,5 @@ +Bugfix: Fix trace ids + +We changed the default tracing to produce non-empty traceids and fixed a problem where traces got disconnected further down the stack. + +https://github.com/owncloud/ocis/pull/8026 diff --git a/changelog/4.0.5_2023-12-21/fix-upload-session-purging.md b/changelog/4.0.5_2023-12-21/fix-upload-session-purging.md new file mode 100644 index 000000000..5ff837037 --- /dev/null +++ b/changelog/4.0.5_2023-12-21/fix-upload-session-purging.md @@ -0,0 +1,5 @@ +Bugfix: Do not purge expired upload sessions that are still postprocessing + +https://github.com/owncloud/ocis/pull/7941 +https://github.com/owncloud/ocis/pull/7859 +https://github.com/owncloud/ocis/pull/7958 \ No newline at end of file diff --git a/changelog/4.0.6_2024-02-07/fix-RED-metrics.md b/changelog/4.0.6_2024-02-07/fix-RED-metrics.md new file mode 100644 index 000000000..89cf681e2 --- /dev/null +++ b/changelog/4.0.6_2024-02-07/fix-RED-metrics.md @@ -0,0 +1,11 @@ +Bugfix: Fix RED metrics on the metrics endpoint + +We connected some metrics to the metrics endpoint to support the RED method for monitoring microservices. + +- Request Rate: The number of requests per second. The total count of requests is available under `ocis_proxy_requests_total`. +- Error Rate: The number of failed requests per second. The total count of failed requests is available under `ocis_proxy_errors_total`. +- Duration: The amount of time each request takes. The duration of all requests is available under `ocis_proxy_request_duration_seconds`. This is a histogram metric, so it also provides information about the distribution of request durations. + +The metrics are available under the following paths: `PROXY_DEBUG_ADDR/metrics` in a prometheus compatible format and maybe secured by `PROXY_DEBUG_TOKEN`. + +https://github.com/owncloud/ocis/pull/7994 diff --git a/changelog/4.0.6_2024-02-07/fix-signed-url-expiry.md b/changelog/4.0.6_2024-02-07/fix-signed-url-expiry.md new file mode 100644 index 000000000..9b0cdf3da --- /dev/null +++ b/changelog/4.0.6_2024-02-07/fix-signed-url-expiry.md @@ -0,0 +1,5 @@ +Bugfix: signed url verification + +Signed urls now expire properly + +https://github.com/owncloud/ocis/pull/8385 diff --git a/changelog/5.0.0-rc.4_2024-02-07/access-token-issuer.md b/changelog/5.0.0-rc.4_2024-02-07/access-token-issuer.md index 3519938ad..fc31a070b 100644 --- a/changelog/5.0.0-rc.4_2024-02-07/access-token-issuer.md +++ b/changelog/5.0.0-rc.4_2024-02-07/access-token-issuer.md @@ -2,4 +2,4 @@ Enhancement: Support spec violating AD FS access token issuer AD FS `/adfs/.well-known/openid-configuration` has an optional `access_token_issuer` which, in violation of the OpenID Connect spec, takes precedence over `issuer`. -https://github.com/owncloud/ocis/pull/7138 +https://github.com/owncloud/ocis/pull/7140 diff --git a/changelog/5.0.0-rc.4_2024-02-07/allow-configuring-additional-routes.md b/changelog/5.0.0-rc.4_2024-02-07/allow-configuring-additional-routes.md index 449d4fcd1..23b548b4c 100644 --- a/changelog/5.0.0-rc.4_2024-02-07/allow-configuring-additional-routes.md +++ b/changelog/5.0.0-rc.4_2024-02-07/allow-configuring-additional-routes.md @@ -1,5 +1,5 @@ Enhancement: Allow configuring additional routes -Allows adding custom routes to the ocis proxy. This enables customs ocis extensions +Allows adding custom routes to the ocis proxy. This enables custom ocis extensions. -https://github.com/owncloud/ocis/pull/7740 +https://github.com/owncloud/ocis/pull/7741 diff --git a/changelog/5.0.0-rc.4_2024-02-07/enhancement-sse-messaging.md b/changelog/5.0.0-rc.4_2024-02-07/enhancement-sse-messaging.md index 31c98eb2a..e43383b85 100644 --- a/changelog/5.0.0-rc.4_2024-02-07/enhancement-sse-messaging.md +++ b/changelog/5.0.0-rc.4_2024-02-07/enhancement-sse-messaging.md @@ -5,6 +5,5 @@ In order to be able to send more content to the client, we have moved the endpoi * notify postprocessing state changes. * notify file locking and unlocking. -* ... @toDo https://github.com/owncloud/ocis/pull/6992 diff --git a/changelog/5.0.0-rc.4_2024-02-07/fix-cs3-backend.md b/changelog/5.0.0-rc.4_2024-02-07/fix-cs3-backend.md index ec72befa0..1dca57590 100644 --- a/changelog/5.0.0-rc.4_2024-02-07/fix-cs3-backend.md +++ b/changelog/5.0.0-rc.4_2024-02-07/fix-cs3-backend.md @@ -1,5 +1,5 @@ -Bugfix: IDP CS3 backend sessions now survire restart +Bugfix: IDP CS3 backend sessions now survive a restart -We now correctly reinitialize the CS3 backend session after the IDP has been restarted. +We now correctly reinitialize the CS3 backend session after the IDP service has been restarted. https://github.com/owncloud/ocis/pull/8142 diff --git a/changelog/5.0.0-rc.4_2024-02-07/passwod-policies-renaming.md b/changelog/5.0.0-rc.4_2024-02-07/passwod-policies-renaming.md index a82ee8af9..8386b9f7f 100644 --- a/changelog/5.0.0-rc.4_2024-02-07/passwod-policies-renaming.md +++ b/changelog/5.0.0-rc.4_2024-02-07/passwod-policies-renaming.md @@ -2,4 +2,4 @@ Enhancement: The password policies change request The variables renaming OCIS-3767 -https://github.com/cs3org/reva/pull/7264 +https://github.com/owncloud/ocis/pull/7264 diff --git a/changelog/unreleased/bump-reva-for-content-disposition-header.md b/changelog/unreleased/bump-reva-for-content-disposition-header.md new file mode 100644 index 000000000..c87050ef2 --- /dev/null +++ b/changelog/unreleased/bump-reva-for-content-disposition-header.md @@ -0,0 +1,7 @@ +Bugfix: Fix Content-Disposition header for downloads + +We have fixed a bug that caused downloads to fail on Chromebased browsers when the filename contained special characters. + +https://github.com/owncloud/ocis/pull/8381 +https://github.com/owncloud/ocis/issues/8361 +https://github.com/cs3org/reva/pull/4498 diff --git a/changelog/unreleased/bump-reva-for-fileincrementor.md b/changelog/unreleased/bump-reva-for-fileincrementor.md new file mode 100644 index 000000000..77ea4e34c --- /dev/null +++ b/changelog/unreleased/bump-reva-for-fileincrementor.md @@ -0,0 +1,6 @@ +Enhancement: bump reva + +We have bumped reve to pull in the changes needed for automatically increment filenames on upload collisions in secret filedrops. + +https://github.com/owncloud/ocis/pull/8340 +https://github.com/owncloud/ocis/issues/8291 diff --git a/changelog/unreleased/enhancement-allow-multiple-event-user-ids.md b/changelog/unreleased/enhancement-allow-multiple-event-user-ids.md new file mode 100644 index 000000000..0fd033cea --- /dev/null +++ b/changelog/unreleased/enhancement-allow-multiple-event-user-ids.md @@ -0,0 +1,6 @@ +Enhancement: allow sending multiple user ids in one sse event + +Sending multiple user ids in one sse event is now possible which reduces the number of sent events. + +https://github.com/owncloud/ocis/pull/8379 +https://github.com/cs3org/reva/pull/4501 diff --git a/changelog/unreleased/enhancement-env-in-yaml.md b/changelog/unreleased/enhancement-env-in-yaml.md new file mode 100644 index 000000000..29cef7756 --- /dev/null +++ b/changelog/unreleased/enhancement-env-in-yaml.md @@ -0,0 +1,15 @@ +Enhancement: Use environment variables in yaml config files + +We added the ability to use environment variables in yaml config files. This allows to use environment variables in the config files of the ocis services which will be replaced by the actual value of the environment variable at runtime. + +Example: + +``` +web: + http: + addr: ${SOME_HTTP_ADDR} +``` + +This makes it possible to use the same config file for different environments without the need to change the config file itself. This is especially useful when using docker-compose to run the ocis services. It is a common pattern to create an .env file which contains the environment variables for the docker-compose file. Now you can use the same .env file to configure the ocis services. + +https://github.com/owncloud/ocis/pull/8339 diff --git a/changelog/unreleased/enhancement-skip-version-service-listing b/changelog/unreleased/enhancement-skip-version-service-listing new file mode 100644 index 000000000..b1e774105 --- /dev/null +++ b/changelog/unreleased/enhancement-skip-version-service-listing @@ -0,0 +1,7 @@ +Enhancement: allow to skip service listing + +The ocis version cmd listed all services by default. This is not always intended, +so we allow to skip the listing of the services by using the --skip-services flag. + +https://github.com/owncloud/ocis/pull/8408 +https://github.com/owncloud/ocis/issues/8070 diff --git a/changelog/unreleased/env-var-annotations.md b/changelog/unreleased/env-var-annotations.md new file mode 100644 index 000000000..98be7169b --- /dev/null +++ b/changelog/unreleased/env-var-annotations.md @@ -0,0 +1,6 @@ +Enhancement: Add a make step to validate the env var annotations + +We have added a make step `make validate-env-var-annotations` to validate the env var annotations in to the environment variables. + +https://github.com/owncloud/ocis/pull/8436 +https://github.com/owncloud/ocis/issues/8258 diff --git a/changelog/unreleased/fix-extended-env-parser.md b/changelog/unreleased/fix-extended-env-parser.md new file mode 100644 index 000000000..4f21c6ddd --- /dev/null +++ b/changelog/unreleased/fix-extended-env-parser.md @@ -0,0 +1,6 @@ +Bugfix: Fix extended env parser + +The extended envvar parser would be angry if there are two `os.Getenv` in the same line. +We fixed this. + +https://github.com/owncloud/ocis/pull/8409 diff --git a/changelog/unreleased/fix-move.md b/changelog/unreleased/fix-move.md new file mode 100644 index 000000000..aabda3a1c --- /dev/null +++ b/changelog/unreleased/fix-move.md @@ -0,0 +1,7 @@ +Bugfix: Fix an error when move + +We fixed a bug that caused Internal Server Error when move using destination id + +https://github.com/owncloud/ocis/pull/8396 +https://github.com/cs3org/reva/pull/4503 +https://github.com/owncloud/ocis/issues/6739 diff --git a/changelog/unreleased/fix-search-error-message.md b/changelog/unreleased/fix-search-error-message.md new file mode 100644 index 000000000..4c65803be --- /dev/null +++ b/changelog/unreleased/fix-search-error-message.md @@ -0,0 +1,6 @@ +Bugfix: Fix search error message + +We fixed an error message returned when the search request is invalid + +https://github.com/owncloud/ocis/pull/8444 +https://github.com/owncloud/ocis/issues/8442 diff --git a/changelog/unreleased/fix-search-responce.md b/changelog/unreleased/fix-search-responce.md new file mode 100644 index 000000000..e7f308a15 --- /dev/null +++ b/changelog/unreleased/fix-search-responce.md @@ -0,0 +1,6 @@ +Bugfix: Fix search response + +We fixed the search response code from 500 to 400 when the request is invalid + +https://github.com/owncloud/ocis/pull/7815 +https://github.com/owncloud/ocis/issues/7812 diff --git a/changelog/unreleased/sharing-ng-expirationdate.md b/changelog/unreleased/sharing-ng-expirationdate.md new file mode 100644 index 000000000..f917b3111 --- /dev/null +++ b/changelog/unreleased/sharing-ng-expirationdate.md @@ -0,0 +1,6 @@ +Bugfix: graph/drives/permission Expiration date update + +We fixed a bug in the Update sharing permission the expiration dates can't be removed from link permissions. + +https://github.com/owncloud/ocis/pull/8413 +https://github.com/owncloud/ocis/issues/8405 diff --git a/changelog/unreleased/speed-up-starttime.md b/changelog/unreleased/speed-up-starttime.md new file mode 100644 index 000000000..15fa90526 --- /dev/null +++ b/changelog/unreleased/speed-up-starttime.md @@ -0,0 +1,5 @@ +Enhancement: Improve ocis single binary start + +Removes waiting times when starting the single binary. Improves ocis single binary boot time from 8s to 2.5s + +https://github.com/owncloud/ocis/pull/8320 diff --git a/changelog/unreleased/update-web-8.0.0.md b/changelog/unreleased/update-web-8.0.0.md new file mode 100644 index 000000000..f30738b62 --- /dev/null +++ b/changelog/unreleased/update-web-8.0.0.md @@ -0,0 +1,159 @@ +Enhancement: Update web to v8.0.0-rc.4 + +Tags: web + +We updated ownCloud Web to v8.0.0-rc.4. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Bugfix [owncloud/web#10489](https://github.com/owncloud/web/pull/10489): Wrong share permissions when resharing off + +We updated ownCloud Web to v8.0.0-rc.3. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Bugfix [owncloud/web#10318](https://github.com/owncloud/web/pull/10318): Scrollable account page +* Bugfix [owncloud/web#10321](https://github.com/owncloud/web/pull/10321): Private link error messages +* Bugfix [owncloud/web#10347](https://github.com/owncloud/web/pull/10347): Readonly user attributes have no effect on group memberships +* Bugfix [owncloud/web#10424](https://github.com/owncloud/web/pull/10424): Restore space +* Enhancement [owncloud/web#10356](https://github.com/owncloud/web/pull/10356): Preview app add reset button for images + +We updated ownCloud Web to v8.0.0-rc.2. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Bugfix [owncloud/web#10176](https://github.com/owncloud/web/pull/10176): Turned off file extensions not always respected +* Bugfix [owncloud/web#10232](https://github.com/owncloud/web/pull/10232): Skip searchbar preview fetch on reload + +We updated ownCloud Web to v8.0.0-rc.1. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Enhancement [owncloud/web#10224](https://github.com/owncloud/web/issues/10224): Harmonize AppSwitcher icon colors +* Bugfix [owncloud/web#10230](https://github.com/owncloud/web/issues/10230): Configurable concurrent requests +* Bugfix [owncloud/web#10158](https://github.com/owncloud/web/issues/10158): GDPR export polling +* Bugfix [owncloud/web#10220](https://github.com/owncloud/web/issues/10220): Loading indicator during conflict dialog +* Bugfix [owncloud/web#10156](https://github.com/owncloud/web/issues/10156): Uploading the same files parallel +* Bugfix [owncloud/web#10179](https://github.com/owncloud/web/issues/10179): Space navigate to trash missing +* Bugfix [owncloud/web#10118](https://github.com/owncloud/web/issues/10118): Tilesview has whitespace +* Bugfix [owncloud/web#10182](https://github.com/owncloud/web/issues/10182): Make versions panel readonly in viewers and editors + +We updated ownCloud Web to v8.0.0-beta.2. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Bugfix [owncloud/web#10010](https://github.com/owncloud/web/issues/10010): Displaying full video in their dimensions +* Bugfix [owncloud/web#10149](https://github.com/owncloud/web/pull/10149): Spaces files list previews cropped +* Bugfix [owncloud/web#10149](https://github.com/owncloud/web/pull/10149): Spaces overview tile previews zoomed +* Bugfix [owncloud/web#10154](https://github.com/owncloud/web/pull/10154): Resolving links without drive alias + +We updated ownCloud Web to v8.0.0-beta.1. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Change [owncloud/web#9698](https://github.com/owncloud/web/pull/9698): Theme handling +* Enhancement [owncloud/web#10111](https://github.com/owncloud/web/pull/10111): Registering right sidebar panels as extension +* Enhancement [owncloud/web#10111](https://github.com/owncloud/web/pull/10111): File sidebar in viewer and editor apps + +We updated ownCloud Web to v8.0.0-alpha.13. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Enhancement [owncloud/web#10104](https://github.com/owncloud/web/pull/10104): Create link modal + +We updated ownCloud Web to v8.0.0-alpha.12. Please refer to the changelog (linked) for details on the web release. + +## Summary +* Bugfix [owncloud/web#9257](https://github.com/owncloud/web/issues/9257): Filter out shares without display name +* Bugfix [owncloud/web#9483](https://github.com/owncloud/web/issues/9483): PDF loading Safari +* Bugfix [owncloud/web#9513](https://github.com/owncloud/web/pull/9513): Set or remove expiration date on group share not possible +* Bugfix [owncloud/web#9529](https://github.com/owncloud/web/pull/9529): Shared with action menu label alignment +* Bugfix [owncloud/web#9587](https://github.com/owncloud/web/pull/9587): Internal public link resolving +* Bugfix [owncloud/web#9593](https://github.com/owncloud/web/issues/9593): Audio- & video-loading on Shared with me page +* Bugfix [owncloud/web#9649](https://github.com/owncloud/web/pull/9649): Add project space filter +* Bugfix [owncloud/web#9663](https://github.com/owncloud/web/pull/9663): Respect the open-in-new-tab-config for external apps +* Bugfix [owncloud/web#9670](https://github.com/owncloud/web/pull/9670): Tiles view accessibility +* Bugfix [owncloud/web#9694](https://github.com/owncloud/web/issues/9694): Special characters in username +* Bugfix [owncloud/web#9788](https://github.com/owncloud/web/issues/9788): Create .space folder if it does not exist +* Bugfix [owncloud/web#9799](https://github.com/owncloud/web/issues/9799): Link resolving into default app +* Bugfix [owncloud/web#9832](https://github.com/owncloud/web/pull/9832): Copy quicklinks for webkit navigator +* Bugfix [owncloud/web#9843](https://github.com/owncloud/web/pull/9843): Fix display path on resources +* Bugfix [owncloud/web#9844](https://github.com/owncloud/web/pull/9844): Upload space image +* Bugfix [owncloud/web#9861](https://github.com/owncloud/web/pull/9861): Duplicated file search request +* Bugfix [owncloud/web#9873](https://github.com/owncloud/web/pull/9873): Tags are no longer editable for a locked file +* Bugfix [owncloud/web#9881](https://github.com/owncloud/web/pull/9881): Prevent rendering of old/wrong set of resources in search list +* Bugfix [owncloud/web#9915](https://github.com/owncloud/web/pull/9915): Keep both folders conflict in same-named folders +* Bugfix [owncloud/web#9931](https://github.com/owncloud/web/pull/9931): Enabling "invite people" for password-protected folder/file +* Bugfix [owncloud/web#10031](https://github.com/owncloud/web/issues/10031): Icon extension mapping +* Bugfix [owncloud/web#10065](https://github.com/owncloud/web/pull/10065): Logout page after token expiry +* Bugfix [owncloud/web#10083](https://github.com/owncloud/web/pull/10083): Disable expiration date for alias link (internal) +* Bugfix [owncloud/web#10092](https://github.com/owncloud/web/pull/10092): Allow empty search query in "in-here" search +* Bugfix [owncloud/web#10096](https://github.com/owncloud/web/pull/10096): Remove password buttons on input if disabled +* Change [owncloud/web#7338](https://github.com/owncloud/web/issues/7338): Remove deprecated code +* Enhancement [owncloud/web#7317](https://github.com/owncloud/ocis/pull/7317): Make login url configurable +* Enhancement [owncloud/web#7497](https://github.com/owncloud/ocis/issues/7497): Permission checks for shares and favorites +* Enhancement [owncloud/web#7600](https://github.com/owncloud/web/issues/7600): Scroll to newly created folder +* Enhancement [owncloud/web#9302](https://github.com/owncloud/web/issues/9302): Application unification +* Enhancement [owncloud/web#9423](https://github.com/owncloud/web/pull/9423): Show local loading spinner in sharing button +* Enhancement [owncloud/web#9441](https://github.com/owncloud/web/pull/9441): File versions tooltip with absolute date +* Enhancement [owncloud/web#9441](https://github.com/owncloud/web/pull/9441): Disabling extensions +* Enhancement [owncloud/web#9451](https://github.com/owncloud/web/pull/9451): Add SSE to get notifications instantly +* Enhancement [owncloud/web#9525](https://github.com/owncloud/web/pull/9525): Tags form improved +* Enhancement [owncloud/web#9527](https://github.com/owncloud/web/pull/9527): Don't display confirmation dialog on file deletion +* Enhancement [owncloud/web#9531](https://github.com/owncloud/web/issues/9531): Personal shares can be shown and hidden +* Enhancement [owncloud/web#9552](https://github.com/owncloud/web/pull/9552): Upload preparation time +* Enhancement [owncloud/web#9561](https://github.com/owncloud/web/pull/9561): Indicate processing state +* Enhancement [owncloud/web#9566](https://github.com/owncloud/web/pull/9566): Display locking information +* Enhancement [owncloud/web#9584](https://github.com/owncloud/web/pull/9584): Moving share's "set expiration date" function +* Enhancement [owncloud/web#9625](https://github.com/owncloud/web/pull/9625): Add keyboard navigation to spaces overview +* Enhancement [owncloud/web#9627](https://github.com/owncloud/web/pull/9627): Add batch actions to spaces +* Enhancement [owncloud/web#9653](https://github.com/owncloud/web/pull/9653): Keyword Query Language (KQL) search syntax +* Enhancement [owncloud/web#9671](https://github.com/owncloud/web/pull/9671): OcModal set buttons to same width +* Enhancement [owncloud/web#9682](https://github.com/owncloud/web/pull/9682): Add password policy compatibility +* Enhancement [owncloud/web#9691](https://github.com/owncloud/web/pull/9691): Password generator for public links +* Enhancement [owncloud/web#9696](https://github.com/owncloud/web/pull/9696): Added app banner for mobile devices +* Enhancement [owncloud/web#9706](https://github.com/owncloud/web/pull/9706): Unify sharing expiration date menu items +* Enhancement [owncloud/web#9727](https://github.com/owncloud/web/pull/9727): Show error if password is on a banned password list +* Enhancement [owncloud/web#9771](https://github.com/owncloud/web/pull/9771): Handle postprocessing state via Server Sent Events +* Enhancement [owncloud/web#9806](https://github.com/owncloud/web/pull/9806): Preview image presentation +* Enhancement [owncloud/web#9809](https://github.com/owncloud/web/pull/9809): Add editors to the application menu +* Enhancement [owncloud/web#9814](https://github.com/owncloud/web/pull/9814): Registering nav items as extension +* Enhancement [owncloud/web#9815](https://github.com/owncloud/web/pull/9815): Add new portal into runtime to include footer +* Enhancement [owncloud/web#9818](https://github.com/owncloud/web/pull/9818): Add `mode` config option +* Enhancement [owncloud/web#9831](https://github.com/owncloud/web/pull/9831): Last modified filter chips +* Enhancement [owncloud/web#9841](https://github.com/owncloud/web/pull/9841): Add embed mode actions +* Enhancement [owncloud/web#9847](https://github.com/owncloud/web/issues/9847): Provide vendor neutral file icons +* Enhancement [owncloud/web#9853](https://github.com/owncloud/web/pull/9853): Show only create folder button in embed mode +* Enhancement [owncloud/web#9854](https://github.com/owncloud/web/pull/9854): Search query term linking +* Enhancement [owncloud/web#9857](https://github.com/owncloud/web/pull/9857): Add permission to delete link passwords when password is enforced +* Enhancement [owncloud/web#9858](https://github.com/owncloud/web/pull/9858): Remove settings icon from searchbar +* Enhancement [owncloud/web#9863](https://github.com/owncloud/web/pull/9863): Location picker in embed mode +* Enhancement [owncloud/web#9864](https://github.com/owncloud/web/pull/9864): Search tags filter chips style aligned +* Enhancement [owncloud/web#9884](https://github.com/owncloud/web/pull/9884): Enable dark theme on importer +* Enhancement [owncloud/web#9890](https://github.com/owncloud/web/pull/9890): Create shortcuts +* Enhancement [owncloud/web#9905](https://github.com/owncloud/web/pull/9905): Manage tags in details panel +* Enhancement [owncloud/web#9906](https://github.com/owncloud/web/pull/9906): Reorganize "New" menu +* Enhancement [owncloud/web#9912](https://github.com/owncloud/web/pull/9912): Add media type filter chip +* Enhancement [owncloud/web#9940](https://github.com/owncloud/web/pull/9940): Display error message for upload to locked folder +* Enhancement [owncloud/web#9966](https://github.com/owncloud/web/issues/9966): Support more audio formats with correct icon +* Enhancement [owncloud/web#10007](https://github.com/owncloud/web/issues/10007): Additional languages +* Enhancement [owncloud/web#10013](https://github.com/owncloud/web/issues/10013): Shared by filter +* Enhancement [owncloud/web#10014](https://github.com/owncloud/web/issues/10014): Share search filter +* Enhancement [owncloud/web#10024](https://github.com/owncloud/web/pull/10024): Duplicate space +* Enhancement [owncloud/web#10037](https://github.com/owncloud/web/pull/10037): Default link permission +* Enhancement [owncloud/web#10047](https://github.com/owncloud/web/pull/10047): Add explaining contextual helper to spaces overview +* Enhancement [owncloud/web#10057](https://github.com/owncloud/web/pull/10057): Folder tree creation during upload +* Enhancement [owncloud/web#10062](https://github.com/owncloud/web/pull/10062): Show webdav information in details view +* Enhancement [owncloud/web#10072](https://github.com/owncloud/web/issues/10072): Add authentication delegation in the Embed mode +* Enhancement [owncloud/web#10099](https://github.com/owncloud/web/pull/10099): Support mandatory filter while listing users +* Enhancement [owncloud/web#10102](https://github.com/owncloud/web/pull/10102): Registering quick actions as extension + +https://github.com/owncloud/ocis/pull/8342 +https://github.com/owncloud/web/releases/tag/v8.0.0-rc.3 +https://github.com/owncloud/ocis/pull/8154 +https://github.com/owncloud/web/releases/tag/v8.0.0-rc.2 +https://github.com/owncloud/ocis/pull/8154 +https://github.com/owncloud/web/releases/tag/v8.0.0-rc.2 +https://github.com/owncloud/ocis/pull/8055 +https://github.com/owncloud/web/releases/tag/v8.0.0-rc.1 +https://github.com/owncloud/ocis/pull/7930 +https://github.com/owncloud/web/releases/tag/v8.0.0-beta.1 +https://github.com/owncloud/ocis/pull/7952 +https://github.com/owncloud/web/releases/tag/v8.0.0-beta.2 +https://github.com/owncloud/ocis/pull/7918 +https://github.com/owncloud/web/releases/tag/v8.0.0-alpha.13 +https://github.com/owncloud/ocis/pull/7883 +https://github.com/owncloud/web/releases/tag/v8.0.0-alpha.12 diff --git a/deployments/continuous-deployment-config/ocis_keycloak/released.yml b/deployments/continuous-deployment-config/ocis_keycloak/released.yml index 8a2d8e3b9..7b4745672 100644 --- a/deployments/continuous-deployment-config/ocis_keycloak/released.yml +++ b/deployments/continuous-deployment-config/ocis_keycloak/released.yml @@ -32,7 +32,7 @@ env: INSECURE: "false" TRAEFIK_ACME_MAIL: wkloucek@owncloud.com - OCIS_DOCKER_TAG: 5.0.0-rc.3 + OCIS_DOCKER_TAG: 4.0.0 OCIS_DOMAIN: ocis.ocis-keycloak.released.owncloud.works KEYCLOAK_DOMAIN: keycloak.ocis-keycloak.released.owncloud.works COMPOSE_FILE: docker-compose.yml:monitoring_tracing/docker-compose-additions.yml diff --git a/deployments/continuous-deployment-config/ocis_ldap/released.yml b/deployments/continuous-deployment-config/ocis_ldap/released.yml index d942ee04f..abba2b35f 100644 --- a/deployments/continuous-deployment-config/ocis_ldap/released.yml +++ b/deployments/continuous-deployment-config/ocis_ldap/released.yml @@ -32,7 +32,7 @@ env: INSECURE: "false" TRAEFIK_ACME_MAIL: wkloucek@owncloud.com - OCIS_DOCKER_TAG: 5.0.0-rc.3 + OCIS_DOCKER_TAG: 4.0.0 OCIS_DOMAIN: ocis.ocis-ldap.released.owncloud.works LDAP_MANAGER_DOMAIN: ldap.ocis-ldap.released.owncloud.works COMPOSE_FILE: docker-compose.yml:monitoring_tracing/docker-compose-additions.yml diff --git a/deployments/continuous-deployment-config/ocis_traefik/released.yml b/deployments/continuous-deployment-config/ocis_traefik/released.yml index c34ef74c9..befd8657b 100644 --- a/deployments/continuous-deployment-config/ocis_traefik/released.yml +++ b/deployments/continuous-deployment-config/ocis_traefik/released.yml @@ -32,7 +32,7 @@ env: INSECURE: "false" TRAEFIK_ACME_MAIL: wkloucek@owncloud.com - OCIS_DOCKER_TAG: 5.0.0-rc.3 + OCIS_DOCKER_TAG: 4.0.0 OCIS_DOMAIN: ocis.ocis-traefik.released.owncloud.works DEMO_USERS: "true" INBUCKET_DOMAIN: mail.ocis-traefik.released.owncloud.works diff --git a/deployments/continuous-deployment-config/ocis_wopi/daily.yml b/deployments/continuous-deployment-config/ocis_wopi/daily.yml index 1ddcf9e11..85c43d21a 100644 --- a/deployments/continuous-deployment-config/ocis_wopi/daily.yml +++ b/deployments/continuous-deployment-config/ocis_wopi/daily.yml @@ -34,6 +34,9 @@ TRAEFIK_ACME_MAIL: wkloucek@owncloud.com OCIS_DOCKER_TAG: latest OCIS_DOMAIN: ocis.ocis-wopi.daily.owncloud.works + COMPANION_DOMAIN: companion.ocis-wopi.daily.owncloud.works + COMPANION_WEB_CONFIG_FILE_NAME: web-daily.yaml + COMPANION_IMAGE: owncloud/uppy-companion:3.12.13-owncloud WOPISERVER_DOMAIN: wopiserver.ocis-wopi.daily.owncloud.works COLLABORA_DOMAIN: collabora.ocis-wopi.daily.owncloud.works ONLYOFFICE_DOMAIN: onlyoffice.ocis-wopi.daily.owncloud.works diff --git a/deployments/continuous-deployment-config/ocis_wopi/latest.yml b/deployments/continuous-deployment-config/ocis_wopi/latest.yml index c98796319..a806c0a4e 100644 --- a/deployments/continuous-deployment-config/ocis_wopi/latest.yml +++ b/deployments/continuous-deployment-config/ocis_wopi/latest.yml @@ -34,6 +34,9 @@ TRAEFIK_ACME_MAIL: wkloucek@owncloud.com OCIS_DOCKER_TAG: latest OCIS_DOMAIN: ocis.ocis-wopi.latest.owncloud.works + COMPANION_DOMAIN: companion.ocis-wopi.latest.owncloud.works + COMPANION_WEB_CONFIG_FILE_NAME: web-latest.yaml + COMPANION_IMAGE: owncloud/uppy-companion:3.12.13-owncloud WOPISERVER_DOMAIN: wopiserver.ocis-wopi.latest.owncloud.works COLLABORA_DOMAIN: collabora.ocis-wopi.latest.owncloud.works ONLYOFFICE_DOMAIN: onlyoffice.ocis-wopi.latest.owncloud.works diff --git a/deployments/continuous-deployment-config/ocis_wopi/released.yml b/deployments/continuous-deployment-config/ocis_wopi/released.yml index cdabbc0a8..c8521d340 100644 --- a/deployments/continuous-deployment-config/ocis_wopi/released.yml +++ b/deployments/continuous-deployment-config/ocis_wopi/released.yml @@ -32,8 +32,11 @@ env: INSECURE: "false" TRAEFIK_ACME_MAIL: wkloucek@owncloud.com - OCIS_DOCKER_TAG: 5.0.0-rc.3 + OCIS_DOCKER_TAG: 4.0.0 OCIS_DOMAIN: ocis.ocis-wopi.released.owncloud.works + COMPANION_DOMAIN: companion.ocis-wopi.released.owncloud.works + COMPANION_IMAGE: owncloud/uppy-companion:3.12.13-owncloud + COMPANION_WEB_CONFIG_FILE_NAME: web-released.yaml WOPISERVER_DOMAIN: wopiserver.ocis-wopi.released.owncloud.works COLLABORA_DOMAIN: collabora.ocis-wopi.released.owncloud.works ONLYOFFICE_DOMAIN: onlyoffice.ocis-wopi.released.owncloud.works diff --git a/deployments/examples/ocis_wopi/.env b/deployments/examples/ocis_wopi/.env index 2a93d76da..2fa535523 100644 --- a/deployments/examples/ocis_wopi/.env +++ b/deployments/examples/ocis_wopi/.env @@ -47,7 +47,7 @@ ONLYOFFICE_DOMAIN= # Inbucket / Mail domain. Defaults to "mail.owncloud.test" INBUCKET_DOMAIN= -### Apache Tika Content analysis toolkit +### Apache Tika Content analysis toolkit ### # Set the desired docker image tag or digest, defaults to "latest" TIKA_IMAGE= @@ -55,3 +55,12 @@ TIKA_IMAGE= # you need uncomment following line. Please see documentation at # https://owncloud.dev/ocis/deployment/monitoring-tracing/ #COMPOSE_FILE=docker-compose.yml:monitoring_tracing/docker-compose-additions.yml + +### Uppy Companion settings ### +# Domain of Uppy Companion. Defaults to "companion.owncloud.test" +COMPANION_IMAGE= +COMPANION_DOMAIN= +COMPANION_WEB_CONFIG_FILE_NAME= +# Provider settings, see https://uppy.io/docs/companion/#provideroptions for reference. Empty by default, which disables providers. +COMPANION_ONEDRIVE_KEY= +COMPANION_ONEDRIVE_SECRET= diff --git a/deployments/examples/ocis_wopi/config/ocis/web-daily.yaml b/deployments/examples/ocis_wopi/config/ocis/web-daily.yaml new file mode 100644 index 000000000..b8890ac32 --- /dev/null +++ b/deployments/examples/ocis_wopi/config/ocis/web-daily.yaml @@ -0,0 +1,18 @@ +web: + config: + external_apps: + - id: preview + path: web-app-preview + config: + mimeTypes: + - image/tiff + - image/bmp + - image/x-ms-bmp + - id: importer + path: web-app-importer + config: + companionUrl: https://companion.ocis-wopi.daily.owncloud.works + supportedClouds: + - WebdavPublicLink + #- OneDrive # needs a client id and secret + #- GoogleDrive # needs a client id and secret and an addition to the DNS zone diff --git a/deployments/examples/ocis_wopi/config/ocis/web-latest.yaml b/deployments/examples/ocis_wopi/config/ocis/web-latest.yaml new file mode 100644 index 000000000..54c7b1b13 --- /dev/null +++ b/deployments/examples/ocis_wopi/config/ocis/web-latest.yaml @@ -0,0 +1,18 @@ +web: + config: + external_apps: + - id: preview + path: web-app-preview + config: + mimeTypes: + - image/tiff + - image/bmp + - image/x-ms-bmp + - id: importer + path: web-app-importer + config: + companionUrl: https://companion.ocis-wopi.latest.owncloud.works + supportedClouds: + - WebdavPublicLink + #- OneDrive # needs a client id and secret + #- GoogleDrive # needs a client id and secret and an addition to the DNS zone diff --git a/deployments/examples/ocis_wopi/config/ocis/web-released.yaml b/deployments/examples/ocis_wopi/config/ocis/web-released.yaml new file mode 100644 index 000000000..1a208841f --- /dev/null +++ b/deployments/examples/ocis_wopi/config/ocis/web-released.yaml @@ -0,0 +1,18 @@ +web: + config: + external_apps: + - id: preview + path: web-app-preview + config: + mimeTypes: + - image/tiff + - image/bmp + - image/x-ms-bmp + - id: importer + path: web-app-importer + config: + companionUrl: https://companion.ocis-wopi.released.owncloud.works + supportedClouds: + - WebdavPublicLink + #- OneDrive # needs a client id and secret + #- GoogleDrive # needs a client id and secret and an addition to the DNS zone diff --git a/deployments/examples/ocis_wopi/docker-compose.yml b/deployments/examples/ocis_wopi/docker-compose.yml index f4ebf3140..ce8137a3e 100644 --- a/deployments/examples/ocis_wopi/docker-compose.yml +++ b/deployments/examples/ocis_wopi/docker-compose.yml @@ -11,6 +11,7 @@ services: - ${WOPISERVER_DOMAIN:-wopiserver.owncloud.test} - ${COLLABORA_DOMAIN:-collabora.owncloud.test} - ${ONLYOFFICE_DOMAIN:-onlyoffice.owncloud.test} + - ${COMPANION_DOMAIN:-companion.owncloud.test} command: - "--log.level=${TRAEFIK_LOG_LEVEL:-ERROR}" # letsencrypt configuration @@ -89,6 +90,7 @@ services: NATS_NATS_PORT: 9233 volumes: - ./config/ocis/app-registry.yaml:/etc/ocis/app-registry.yaml + - ./config/ocis/${COMPANION_WEB_CONFIG_FILE_NAME:-web.yaml}:/etc/ocis/web.yaml - ocis-config:/etc/ocis - ocis-data:/var/lib/ocis labels: @@ -245,6 +247,32 @@ services: ocis-net: restart: always + companion: + image: ${COMPANION_IMAGE:-transloadit/companion:4.5.1} + networks: + ocis-net: + environment: + NODE_ENV: production + NODE_TLS_REJECT_UNAUTHORIZED: 0 + COMPANION_DATADIR: /tmp/companion/ + COMPANION_DOMAIN: ${COMPANION_DOMAIN:-companion.owncloud.test} + COMPANION_PROTOCOL: https + COMPANION_UPLOAD_URLS: "^https://${OCIS_DOMAIN:-ocis.owncloud.test}/" + COMPANION_ONEDRIVE_KEY: "${COMPANION_ONEDRIVE_KEY}" + COMPANION_ONEDRIVE_SECRET: "${COMPANION_ONEDRIVE_SECRET}" + volumes: + - companion-data:/tmp/companion/ + labels: + - "traefik.enable=true" + - "traefik.http.routers.companion.entrypoints=https" + - "traefik.http.routers.companion.rule=Host(`${COMPANION_DOMAIN:-companion.owncloud.test}`)" + - "traefik.http.routers.companion.tls.certresolver=http" + - "traefik.http.routers.companion.service=companion" + - "traefik.http.services.companion.loadbalancer.server.port=3020" + logging: + driver: "local" + restart: always + inbucket: image: inbucket/inbucket networks: @@ -273,6 +301,7 @@ volumes: ocis-config: ocis-data: wopi-recovery: + companion-data: networks: ocis-net: diff --git a/docs/architecture/_index.md b/docs/architecture/_index.md index 968452f16..e4ed80497 100644 --- a/docs/architecture/_index.md +++ b/docs/architecture/_index.md @@ -1,7 +1,7 @@ --- title: Architecture date: 2023-12-06T13:00:00+01:00 -weight: 25 +weight: 10 geekdocRepo: https://github.com/owncloud/ocis geekdocEditPath: edit/master/docs/architecture geekdocFilePath: _index.md diff --git a/docs/helpers/extendedEnv.go b/docs/helpers/extendedEnv.go index ad0904287..3dee523b9 100644 --- a/docs/helpers/extendedEnv.go +++ b/docs/helpers/extendedEnv.go @@ -85,18 +85,20 @@ func GetRogueEnvs() { } res := re.FindAllSubmatch([]byte(r[1]), -1) - if len(res) != 1 || len(res[0]) < 2 { + if len(res) < 1 { fmt.Printf("Error envvar not matching pattern: %s", r[1]) continue } - path := r[0] - name := strings.Trim(string(res[0][1]), "\"") - currentVars[path+name] = Variable{ - RawName: name, - Path: path, - FoundInCode: true, - Name: name, + for _, m := range res { + path := r[0] + name := strings.Trim(string(m[1]), "\"") + currentVars[path+name] = Variable{ + RawName: name, + Path: path, + FoundInCode: true, + Name: name, + } } } diff --git a/docs/helpers/extended_vars.yaml b/docs/helpers/extended_vars.yaml index a55ea1f37..28091b1a8 100644 --- a/docs/helpers/extended_vars.yaml +++ b/docs/helpers/extended_vars.yaml @@ -32,19 +32,19 @@ variables: default_value: "" description: "" do_ignore: true -- rawname: registryEnv - path: ocis-pkg/registry/registry.go:118 +- rawname: _registryEnv + path: ocis-pkg/registry/registry.go:114 foundincode: true name: MICRO_REGISTRY type: string - default_value: "" + default_value: nats-js-kv description: 'The Go micro registry type to use. Supported types are: ''memory'', ''nats-js-kv'' (default) and ''kubernetes''. Note that ''nats'', ''etcd'', ''consul'' and ''mdns'' are deprecated and will be removed in a later version. Only change on supervision of ownCloud Support.' do_ignore: false -- rawname: registryAddressEnv - path: ocis-pkg/registry/registry.go:122 +- rawname: _registryAddressEnv + path: ocis-pkg/natsjsregistry/registry.go:145 foundincode: true name: MICRO_REGISTRY_ADDRESS type: string @@ -52,19 +52,27 @@ variables: description: The bind address of the internal go micro framework. Only change on supervision of ownCloud Support. do_ignore: false -- rawname: registryPasswordEnv - path: ocis-pkg/registry/registry.go:115 +- rawname: _registryAddressEnv + path: ocis-pkg/registry/registry.go:118 + foundincode: true + name: MICRO_REGISTRY_ADDRESS + type: "" + default_value: "" + description: "" + do_ignore: true +- rawname: _registryPasswordEnv + path: ocis-pkg/natsjsregistry/registry.go:163 foundincode: true name: MICRO_REGISTRY_AUTH_PASSWORD - type: "" + type: string default_value: "" description: Optional when using nats to authenticate with the nats cluster. do_ignore: false -- rawname: registryUsernameEnv - path: ocis-pkg/registry/registry.go:114 +- rawname: _registryUsernameEnv + path: ocis-pkg/natsjsregistry/registry.go:163 foundincode: true name: MICRO_REGISTRY_AUTH_USERNAME - type: "" + type: string default_value: "" description: Optional when using nats to authenticate with the nats cluster. do_ignore: false @@ -73,21 +81,20 @@ variables: foundincode: true name: OCIS_BASE_DATA_PATH type: string - default_value: '''/var/lib/ocis'' or ''$HOME/.ocis/''' + default_value: "" description: The base directory location used by several services and for user data. - Predefined to '/var/lib/ocis' for container images (inside the container) or '$HOME/.ocis/' - for binary releases. Services can have, if available, an individual setting with - an own environment variable. + See the General Info section in the documentation for more details on defaults. + Services can have, if available, an individual setting with an own environment + variable. do_ignore: false - rawname: OCIS_CONFIG_DIR path: ocis-pkg/config/defaults/paths.go:56 foundincode: true name: OCIS_CONFIG_DIR type: string - default_value: '''/etc/ocis'' or ''$HOME/.ocis/config''' - description: The default directory location for config files. Predefined to '/etc/ocis' - for container images (inside the container) or '$HOME/.ocis/config' for binary - releases. + default_value: "" + description: The default directory location for config files. See the General Info + section in the documentation for more details on defaults. do_ignore: false - rawname: parts[0] path: ocis-pkg/config/envdecode/envdecode.go:382 diff --git a/docs/services/_includes/.gitignore b/docs/services/_includes/.gitignore index 02c620656..9d0d8ab10 100644 --- a/docs/services/_includes/.gitignore +++ b/docs/services/_includes/.gitignore @@ -1,3 +1,4 @@ *_configvars.md *-example.yaml -adoc/*adoc \ No newline at end of file +adoc/*adoc +adoc/env-var-deltas/*adoc diff --git a/docs/services/_includes/adoc/env-var-deltas/.gitkeep b/docs/services/_includes/adoc/env-var-deltas/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.adoc b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.adoc new file mode 100644 index 000000000..14b6b444f --- /dev/null +++ b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.adoc @@ -0,0 +1,885 @@ +// # Added Variables between oCIS 4.0.0 and oCIS 5.0.0 +// commenting the headline to make it better includable + +// table created based on the .md file per 2024.02.12 +// the table should be recreated/updated based on the final .md file + +[width="100%",cols="~,~,~,~",options="header"] +|=== +| Service| Variable| Description| Default + +| xref:deployment/services/env-vars-special-scope.adoc[Special Scope Envvars] +| `OCIS_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured.| `false` +| +| `MICRO_REGISTRY` (important change) +| The Go micro registry type to use. Supported types are: 'memory', 'nats-js-kv' (default) and 'kubernetes'. Note that 'nats', 'etcd', 'consul' and 'mdns' are deprecated and will be removed in a later version. Only change on supervision of ownCloud Support.| nats-js-kv +| +| `MICRO_REGISTRY_AUTH_PASSWORD` +| Optional when using nats to authenticate with the nats cluster.| +| +| `MICRO_REGISTRY_AUTH_USERNAME` +| Optional when using nats to authenticate with the nats cluster.| + +| xref:{s-path}/antivirus.adoc[antivirus] +| `OCIS_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `ANTIVIRUS_ICAP_SCAN_TIMEOUT` +| Scan timeout for the ICAP client. Defaults to '5m' (5 minutes). See the Environment Variable Types description for more details.| 5m0s + +| xref:{s-path}/audit.adoc[audit] +| `OCIS_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| + +| xref:{s-path}/auth-service.adoc[auth-service] +| `OCIS_TRACING_ENABLED` + +`AUTH_SERVICE_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`AUTH_SERVICE_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."| +| +| `OCIS_TRACING_ENDPOINT` + +`AUTH_SERVICE_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`AUTH_SERVICE_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| +| +| `OCIS_LOG_LEVEL` + +`AUTH_SERVICE_LOG_LEVEL` +| The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."| +| +| `OCIS_LOG_PRETTY` + +`AUTH_SERVICE_LOG_PRETTY` +| Activates pretty log output.| +| +| `OCIS_LOG_COLOR` + +`AUTH_SERVICE_LOG_COLOR` +| Activates colorized log output.| +| +| `OCIS_LOG_FILE` + +`AUTH_SERVICE_LOG_FILE` +| The path to the log file. Activates logging to this file if set.| +| +| `AUTH_SERVICE_DEBUG_ADDR` +| Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed.| +| +| `AUTH_SERVICE_DEBUG_TOKEN` +| Enables pprof, which can be used for profiling.| +| +| `AUTH_SERVICE_DEBUG_ZPAGES` +| Enables zpages, which can be used for collecting and viewing in-memory traces.| +| +| `AUTH_SERVICE_GRPC_ADDR` +| The bind address of the GRPC service.| +| +| `AUTH_SERVICE_GRPC_PROTOCOL` +| The transport protocol of the GRPC service.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`AUTH_SERVICE_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`AUTH_SERVICE_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| +| `OCIS_JWT_SECRET` + +`AUTH_SERVICE_JWT_SECRET` +| The secret to mint and validate jwt tokens.| + +| xref:{s-path}/clientlog.adoc[clientlog] +| `OCIS_REVA_GATEWAY` +| CS3 gateway used to look up user metadata| +| +| `OCIS_EVENTS_ENDPOINT` + +`CLIENTLOG_EVENTS_ENDPOINT` +| The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture.| +| +| `OCIS_EVENTS_CLUSTER` + +`CLIENTLOG_EVENTS_CLUSTER` +| The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system.| +| +| `OCIS_INSECURE` + +`CLIENTLOG_EVENTS_TLS_INSECURE` +| Whether to verify the server TLS certificates.| +| +| `OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE` + +`CLIENTLOG_EVENTS_TLS_ROOT_CA_CERTIFICATE` +| The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false.| +| +| `OCIS_EVENTS_ENABLE_TLS` + +`CLIENTLOG_EVENTS_ENABLE_TLS` +| Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`CLIENTLOG_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`CLIENTLOG_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_JWT_SECRET` + +`CLIENTLOG_JWT_SECRET` +| The secret to mint and validate jwt tokens.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`CLIENTLOG_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`CLIENTLOG_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| +| `CLIENTLOG_DEBUG_ADDR` +| Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed.| +| +| `CLIENTLOG_DEBUG_TOKEN` +| Token to secure the metrics endpoint.| +| +| `CLIENTLOG_DEBUG_PPROF` +| Enables pprof, which can be used for profiling.| +| +| `CLIENTLOG_DEBUG_ZPAGES` +| Enables zpages, which can be used for collecting and viewing in-memory traces.| + +| +| `OCIS_LOG_LEVEL` + +`CLIENTLOG_USERLOG_LOG_LEVEL` +| The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."| +| +| `OCIS_LOG_PRETTY` + +`CLIENTLOG_USERLOG_LOG_PRETTY` +| Activates pretty log output.| +| +| `OCIS_LOG_COLOR` + +`CLIENTLOG_USERLOG_LOG_COLOR` +| Activates colorized log output.| +| +| `OCIS_LOG_FILE` + +`CLIENTLOG_USERLOG_LOG_FILE` +| The path to the log file. Activates logging to this file if set.| + +| +| `OCIS_TRACING_ENABLED` + +`CLIENTLOG_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`CLIENTLOG_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` +| +| +| `OCIS_TRACING_ENDPOINT` + +`CLIENTLOG_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`CLIENTLOG_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| + +| xref:{s-path}/eventhistory.adoc[eventhistory] +| `OCIS_PERSISTENT_STORE_AUTH_USERNAME` + +`EVENTHISTORY_STORE_AUTH_USERNAME` +| The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_PERSISTENT_STORE_AUTH_PASSWORD` + +`EVENTHISTORY_STORE_AUTH_PASSWORD` +| The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`EVENTHISTORY_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`EVENTHISTORY_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| + +| xref:{s-path}/frontend.adoc[frontend] +| `OCIS_ENABLE_RESHARING` +| Changing this value is NOT supported. Enables the support for resharing in the clients.| +| +| `FRONTEND_DEFAULT_LINK_PERMISSIONS` +| Defines the default permissions a link is being created with. Possible values are 0 (= internal link, for instance members only) and 1 (= public link with viewer permissions). Defaults to 1.| +| +| `FRONTEND_AUTO_ACCEPT_SHARES` +| Defines if shares should be auto accepted by default. Users can change this setting individually in their profile.| true +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`FRONTEND_OCS_STAT_CACHE_DISABLE_PERSISTENCE` +| Disable persistence of the cache. Only applies when using the 'nats-js-kv' store type. Defaults to false.| false +| +| `FRONTEND_OCS_LIST_OCM_SHARES` +| Include OCM shares when listing shares. See the OCM service documentation for more details.| +| +| `OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD` + +`FRONTEND_OCS_PUBLIC_SHARE_MUST_HAVE_PASSWORD` +| Set this to true if you want to enforce passwords on all public shares.| +| +| `FRONTEND_OCS_INCLUDE_OCM_SHAREES` +| nclude OCM sharees when listing sharees.| +| +| `OCIS_EVENTS_ENDPOINT` + +`FRONTEND_EVENTS_ENDPOINT` +| The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture.| +| +| `OCIS_EVENTS_CLUSTER` + +`FRONTEND_EVENTS_CLUSTER` +| The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system.| +| +| `OCIS_INSECURE` + +`FRONTEND_EVENTS_TLS_INSECURE` +| Whether to verify the server TLS certificates.| +| +| `FRONTEND_EVENTS_TLS_ROOT_CA_CERTIFICATE` + +`OCS_EVENTS_TLS_ROOT_CA_CERTIFICATE` +| The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false.| +| +| `OCIS_EVENTS_ENABLE_TLS` + +`FRONTEND_EVENTS_ENABLE_TLS` +| Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`FRONTEND_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`FRONTEND_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`FRONTEND_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`FRONTEND_SERVICE_ACCOUNT_SECRET` +| The service account secret.| +| +| `OCIS_PASSWORD_POLICY_MIN_CHARACTERS` + +`FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS` +| Define the minimum password length. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` + +`FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` +| Define the minimum number of lowercase characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` + +`FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` +| Define the minimum number of uppercase characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_DIGITS` + +`FRONTEND_PASSWORD_POLICY_MIN_DIGITS` +| Define the minimum number of digits. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` + +`FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` +| Define the minimum number of special characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` + +`FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` +| Path to the 'banned passwords list' file. See the documentation for more details.| + +| xref:{s-path}/gateway.adoc[gateway] +| `GATEWAY_STORAGE_REGISTRY_DRIVER` +| The driver name of the storage registry to use.| +| +| `GATEWAY_STORAGE_REGISTRY_RULES` +| The rules for the storage registry. See the Environment Variable Types description for more details.| +| +| `GATEWAY_STORAGE_REGISTRY_CONFIG_JSON` +| Additional configuration for the storage registry in json format.| +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`GATEWAY_STAT_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the stat cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`GATEWAY_PROVIDER_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the provider cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`GATEWAY_CREATE_HOME_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the create home cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false + +| xref:{s-path}/graph.adoc[graph] +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`GRAPH_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_CACHE_AUTH_USERNAME` + +`GRAPH_CACHE_AUTH_USERNAME` +| The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_CACHE_AUTH_PASSWORD` + +`GRAPH_CACHE_AUTH_PASSWORD` +| The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| + +| +| `GRAPH_IDENTITY_SEARCH_MIN_LENGTH` +| The minimum length the search term needs to have for unprivileged users when searching for users or groups.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`GRAPH_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`GRAPH_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`GRAPH_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`GRAPH_SERVICE_ACCOUNT_SECRET` +| The service account secret.| +| +| `OCIS_ENABLE_RESHARING` + +`GRAPH_ENABLE_RESHARING` +| Changing this value is NOT supported. Enables the support for resharing in the clients.| + +| xref:{s-path}/notifications.adoc[notifications] +| `OCIS_DEFAULT_LANGUAGE` +| The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`NOTIFICATIONS_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`NOTIFICATIONS_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`NOTIFICATIONS_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`NOTIFICATIONS_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| xref:{s-path}/ocdav.adoc[ocdav] +| `OCDAV_OCM_NAMESPACE` +| The human readable path prefix for the ocm shares.| + +| xref:{s-path}/ocm.adoc[ocm] +| `SHARING_OCM_PROVIDER_AUTHORIZER_DRIVER` +| Driver to be used to persist ocm invites. Supported value is only 'json'.| `json` +| +| `OCM_HTTP_ADDR` +| The bind address of the HTTP service.| +| +| `OCM_HTTP_PROTOCOL` +| The transport protocol of the HTTP service.| +| +| `OCM_HTTP_PREFIX` +| The path prefix where OCM can be accessed (defaults to /).| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`OCM_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`OCM_SERVICE_ACCOUNT_SECRET` +| The service account secret.| +| +| `OCIS_CORS_ALLOW_ORIGINS` + +`OCM_CORS_ALLOW_ORIGINS` +| A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_METHODS` + +`OCM_CORS_ALLOW_METHODS` +| A list of allowed CORS methods. See following chapter for more details: *Access-Control-Allow-Methods* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_HEADERS` + +`OCM_CORS_ALLOW_HEADERS` +| A list of allowed CORS headers. See following chapter for more details: *Access-Control-Allow-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_CREDENTIALS` + +`OCM_CORS_ALLOW_CREDENTIALS` +| Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials.| +| +| `OCM_GRPC_ADDR` +| The bind address of the GRPC service.| +| +| `OCM_GRPC_PROTOCOL` +| The transport protocol of the GRPC service.| +| +| `OCM_SCIENCEMESH_PREFIX` +| URL path prefix for the ScienceMesh service. Note that the string must not start with '/'.| +| +| `OCM_MESH_DIRECTORY_URL` +| URL of the mesh directory service.| +| +| `OCM_OCMD_PREFIX` +| URL path prefix for the OCMd service. Note that the string must not start with '/'.| +| +| `OCM_OCMD_EXPOSE_RECIPIENT_DISPLAY_NAME` +| Expose the display name of OCM share recipients.| +| +| `OCM_OCM_INVITE_MANAGER_DRIVER` +| Driver to be used to persist ocm invites. Supported value is only 'json'.| `json` +| +| `OCM_OCM_INVITE_MANAGER_INSECURE` +| Disable TLS certificate validation for the OCM connections. Do not set this in production environments.| +| +| `OCM_OCM_INVITE_MANAGER_JSON_FILE` +| Path to the JSON file where OCM invite data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage.| +| +| `OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE` +| Path to the JSON file where ocm invite data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage.| +| +| `OCM_OCM_PROVIDER_AUTHORIZER_VERIFY_REQUEST_HOSTNAME` +| Verify the hostname of the request against the hostname of the OCM provider.| +| +| `OCM_OCM_CORE_DRIVER` +| Driver to be used to persist ocm shares. Supported value is only 'json'.| `json` +| +| `OCM_OCM_STORAGE_PROVIDER_INSECURE` +| Disable TLS certificate validation for the OCM connections. Do not set this in production environments.| +| +| `OCM_OCM_STORAGE_PROVIDER_STORAGE_ROOT` +| Directory where the ocm storage provider persists its data like tus upload info files.| +| +| `OCM_OCM_CORE_JSON_FILE` +| Path to the JSON file where OCM share data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage.| +| +| `OCM_OCM_SHARE_PROVIDER_DRIVER` +| Driver to be used to persist ocm shares. Supported value is only 'json'.| `json` +| +| `OCM_OCM_SHARE_PROVIDER_INSECURE` +| Disable TLS certificate validation for the OCM connections. Do not set this in production environments.| +| +| `OCM_WEBAPP_TEMPLATE` +| 'Template for the webapp url.| +| +| `OCM_OCM_SHAREPROVIDER_JSON_FILE` +| Path to the JSON file where OCM share data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage.| + +| +| `OCM_DEBUG_ADDR` +| Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed.| +| +| `OCM_DEBUG_TOKEN` +| Token to secure the metrics endpoint.| +| +| `OCM_DEBUG_PPROF` +| Enables pprof, which can be used for profiling.| +| +| `OCM_DEBUG_ZPAGES` +| Enables zpages, which can be used for collecting and viewing in-memory traces.| + +| +| `OCIS_LOG_LEVEL` + +`OCM_LOG_LEVEL` +| The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."| +| +| `OCIS_LOG_PRETTY` + +`OCM_LOG_PRETTY` +| Activates pretty log output.| +| +| `OCIS_LOG_COLOR` + +`OCM_LOG_COLOR` +| Activates colorized log output.| +| +| `OCIS_LOG_FILE` + +`OCM_LOG_FILE` +| The path to the log file. Activates logging to this file if set.| + +| +| `OCIS_TRACING_ENABLED` + +`OCM_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`OCM_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` +| +| +| `OCIS_TRACING_ENDPOINT` + +`OCM_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`OCM_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| + +| xref:{s-path}/policies.adoc[policies] +| `OCIS_EVENTS_AUTH_USERNAME` + +`POLICIES_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`POLICIES_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| + +| +| `OCIS_TRACING_ENABLED` + +`POLICIES_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`POLICIES_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` +| +| +| `OCIS_TRACING_ENDPOINT` + +`POLICIES_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`POLICIES_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| + +| xref:{s-path}/postprocessing.adoc[postprocessing] +| `POSTPROCESSING_RETRY_BACKOFF_DURATION` +| The base for the exponential backoff duration before retrying a failed postprocessing step. See the Environment Variable Types description for more details.| +| +| `POSTPROCESSING_MAX_RETRIES` +| The maximum number of retries for a failed postprocessing step.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`POSTPROCESSING_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`POSTPROCESSING_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_PERSISTENT_STORE_AUTH_USERNAME` + +`POSTPROCESSING_STORE_AUTH_USERNAME` +| The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_PERSISTENT_STORE_AUTH_PASSWORD` + +`POSTPROCESSING_STORE_AUTH_PASSWORD` +| The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| + +| +| `OCIS_TRACING_ENABLED` + +`POSTPROCESSING_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`POSTPROCESSING_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` +| +| +| `OCIS_TRACING_ENDPOINT` + +`POSTPROCESSING_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`POSTPROCESSING_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| + +| xref:{s-path}/proxy.adoc[proxy] +| `OCIS_CACHE_AUTH_USERNAME` + +`PROXY_OIDC_USERINFO_CACHE_AUTH_USERNAME` +| The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_CACHE_AUTH_PASSWORD` + +`PROXY_OIDC_USERINFO_CACHE_AUTH_PASSWORD` +| The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`PROXY_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`PROXY_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| xref:{s-path}/search.adoc[search] +| `OCIS_SERVICE_ACCOUNT_ID` + +`SEARCH_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`SEARCH_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| +| `SEARCH_EXTRACTOR_TIKA_CLEAN_STOP_WORDS` +| Defines if stop words should be cleaned or not. See the documentation for more details.| + +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`SEARCH_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`SEARCH_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| + +| xref:{s-path}/settings.adoc[settings] +| `SETTINGS_SERVICE_ACCOUNT_IDS` + +`OCIS_SERVICE_ACCOUNT_ID` +| The list of all service account IDs. These will be assigned the hidden 'service-account' role. Note: When using 'OCIS_SERVICE_ACCOUNT_ID' this will contain only one value while 'SETTINGS_SERVICE_ACCOUNT_IDS' can have multiple. See the 'auth-service' service description for more details about service accounts.| +| +| `OCIS_DEFAULT_LANGUAGE` +| The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details.| +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`SETTINGS_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`SETTINGS_EVENTS_AUTH_USERNAME` +| The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`SETTINGS_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured.| + +| xref:{s-path}/sharing.adoc[sharing] +| `OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD` + +`SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD` +| Set this to true if you want to enforce passwords on all public shares.| +| +| `OCIS_PASSWORD_POLICY_MIN_CHARACTERS` + +`SHARING_PASSWORD_POLICY_MIN_CHARACTERS` +| Define the minimum password length. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` + +`SHARING_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` +| Define the minimum number of lowercase characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` + +`SHARING_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` +| Define the minimum number of uppercase characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_DIGITS` + +`SHARING_PASSWORD_POLICY_MIN_DIGITS` +| Define the minimum number of digits. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` + +`SHARING_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` +| Define the minimum number of special characters. Defaults to 0 if not set.| 0 +| +| `OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` + +`SHARING_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` +| Path to the 'banned passwords list' file. See the documentation for more details.| + +| xref:{s-path}/sse.adoc[sse] +| `OCIS_LOG_LEVEL` + +`SSE_LOG_LEVEL` +| The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."| +| +| `OCIS_LOG_PRETTY` + +`SSE_LOG_PRETTY` +| Activates pretty log output.| +| +| `OCIS_LOG_COLOR` + +`SSE_LOG_COLOR` +| Activates colorized log output.| +| +| `OCIS_LOG_FILE` + +`SSE_LOG_FILE` +| The path to the log file. Activates logging to this file if set.| +| +| `SSE_DEBUG_ADDR` +| Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed.| +| +| `SSE_DEBUG_TOKEN` +| Token to secure the metrics endpoint.| +| +| `SSE_DEBUG_PPROF` +| Enables pprof, which can be used for profiling.| +| +| `SSE_DEBUG_ZPAGES` +| Enables zpages, which can be used for collecting and viewing in-memory traces.| +| +| `OCIS_EVENTS_ENDPOINT` + +`SSE_EVENTS_ENDPOINT` +| The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture.| +| +| `OCIS_EVENTS_CLUSTER` + +`SSE_EVENTS_CLUSTER` +| The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system.| +| +| `OCIS_INSECURE` + +`SSE_EVENTS_TLS_INSECURE` +| Whether to verify the server TLS certificates.| +| +| `OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE` + +`SSE_EVENTS_TLS_ROOT_CA_CERTIFICATE` +| The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false.| +| +| `OCIS_EVENTS_ENABLE_TLS` + +`SSE_EVENTS_ENABLE_TLS` +| Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`SSE_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`SSE_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_CORS_ALLOW_ORIGINS` + +`SSE_CORS_ALLOW_ORIGINS` +| A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_METHODS` + +`SSE_CORS_ALLOW_METHODS` +| A list of allowed CORS methods. See following chapter for more details: *Access-Control-Allow-Methods* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_HEADERS` + +`SSE_CORS_ALLOW_HEADERS` +| A list of allowed CORS headers. See following chapter for more details: *Access-Control-Allow-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers. See the Environment Variable Types description for more details.| +| +| `OCIS_CORS_ALLOW_CREDENTIALS` + +`SSE_CORS_ALLOW_CREDENTIALS` +| Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials.| +| +| `SSE_HTTP_ADDR` +| The bind address of the HTTP service. | +| +| `SSE_HTTP_ROOT` +| The root path of the HTTP service.| +| +| `OCIS_JWT_SECRET` + +`SSE_JWT_SECRET` +| The secret to mint and validate jwt tokens.| + +| +| `OCIS_TRACING_ENABLED` + +`SSE_TRACING_ENABLED` +| Activates tracing.| +| +| `OCIS_TRACING_TYPE` + +`SSE_TRACING_TYPE` +| The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` +| +| +| `OCIS_TRACING_ENDPOINT` + +`SSE_TRACING_ENDPOINT` +| The endpoint of the tracing agent.| +| +| `OCIS_TRACING_COLLECTOR` + +`SSE_TRACING_COLLECTOR` +| The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset.| + +| xref:{s-path}/storage-system.adoc[storage-system] +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`STORAGE_SYSTEM_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false + +| xref:{s-path}/storage-users.adoc[storage-users] +| `OCIS_GATEWAY_GRPC_ADDR` + +`STORAGE_USERS_GATEWAY_GRPC_ADDR` +| The bind address of the gateway GRPC address.| +| +| `OCIS_MACHINE_AUTH_API_KEY` +| Machine auth API key used to validate internal requests necessary for the access to resources from other services.| +| +| `STORAGE_USERS_CLI_MAX_ATTEMPTS_RENAME_FILE` +| The maximum number of attempts to rename a file when a user restores a file to an existing destination with the same name. The minimum value is 100.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`STORAGE_USERS_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`STORAGE_USERS_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`STORAGE_USERS_STAT_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the stat cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`STORAGE_USERS_FILEMETADATA_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the file metadata cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_CACHE_DISABLE_PERSISTENCE` + +`STORAGE_USERS_ID_CACHE_DISABLE_PERSISTENCE` +| Disables persistence of the id cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false.| false +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`STORAGE_USERS_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`STORAGE_USERS_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| xref:{s-path}/userlog.adoc[userlog] +| `OCIS_DEFAULT_LANGUAGE` +| The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details.| +| +| `OCIS_PERSISTENT_STORE_AUTH_USERNAME` + +`USERLOG_STORE_AUTH_USERNAME` +| The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_PERSISTENT_STORE_AUTH_PASSWORD` + +`USERLOG_STORE_AUTH_PASSWORD` +| The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured.| +| +| `OCIS_EVENTS_AUTH_USERNAME` + +`USERLOG_EVENTS_AUTH_USERNAME` +| The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_EVENTS_AUTH_PASSWORD` + +`USERLOG_EVENTS_AUTH_PASSWORD` +| The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.| +| +| `OCIS_SERVICE_ACCOUNT_ID` + +`USERLOG_SERVICE_ACCOUNT_ID` +| The ID of the service account the service should use. See the 'auth-service' service description for more details.| +| +| `OCIS_SERVICE_ACCOUNT_SECRET` + +`USERLOG_SERVICE_ACCOUNT_SECRET` +| The service account secret.| + +| xref:{s-path}/web.adoc[web] +| `WEB_OPTION_LOGIN_URL` +| Specifies the target URL to the login page. This is helpful when an external IdP is used. This option is disabled by default. Example URL like: https://www.myidp.com/login.| +| +| `WEB_OPTION_DISABLED_EXTENSIONS` +| A list to disable specific Web extensions identified by their ID. The ID can e.g. be taken from the 'index.ts' file of the web extension. Example: 'com.github.owncloud.web.files.search,com.github.owncloud.web.files.print'. See the Environment Variable Types description for more details.| +| +| `WEB_OPTION_USER_LIST_REQUIRES_FILTER` +| Defines whether one or more filters must be set in order to list users in the Web admin settings. Set this option to 'true' if running in an environment with a lot of users and listing all users could slow down performance. Defaults to 'false'.| false +| +| `WEB_OPTION_EMBED_ENABLED` +| Defines whether Web should be running in 'embed' mode. Setting this to 'true' will enable a stripped down version of Web with reduced functionality used to integrate Web into other applications like via iFrame. Setting it to 'false' or not setting it (default) will run Web as usual with all functionality enabled. See the text description for more details.| +| +| `WEB_OPTION_EMBED_TARGET` +| Defines how Web is being integrated when running in 'embed' mode. Currently, the only supported options are '' (empty) and 'location'. With '' which is the default, Web will run regular as defined via the 'embed.enabled' config option. With 'location', Web will run embedded as location picker. Resource selection will be disabled and the selected resources array always includes the current folder as the only item. See the text description for more details. | +| +| `WEB_OPTION_EMBED_MESSAGES_ORIGIN` +| Defines a URL under which Web can be integrated via iFrame in 'embed' mode. Note that setting this is mandatory when running Web in 'embed' mode. Use '*' as value to allow running the iFrame under any URL, although this is not recommended for security reasons. See the text description for more details.| +| +| `WEB_OPTION_EMBED_DELEGATE_AUTHENTICATION` +| Defines whether Web should require authentication to be done by the parent application when running in 'embed' mode. If set to 'true' Web will not try to authenticate the user on its own but will require an access token coming from the parent application. Defaults to being unset.| +| +| `WEB_OPTION_EMBED_DELEGATE_AUTHENTICATION_ORIGIN` +| Defines the host to validate the message event origin against when running Web in 'embed' mode with delegated authentication. Defaults to event message origin validation being omitted, which is only recommended for development setups.| +| +| `WEB_OPTION_CONCURRENT_REQUESTS_RESOURCE_BATCH_ACTIONS` +| Defines the maximum number of concurrent requests per file/folder/space batch action. Defaults to 4.| +| +| `WEB_OPTION_CONCURRENT_REQUESTS_SSE` +| Defines the maximum number of concurrent requests in SSE event handlers. Defaults to 4.| +| +| `WEB_OPTION_CONCURRENT_REQUESTS_SHARES_CREATE` +| Defines the maximum number of concurrent requests per sharing invite batch. Defaults to 4.| +| +| `WEB_OPTION_CONCURRENT_REQUESTS_SHARES_LIST` +| Defines the maximum number of concurrent requests when loading individual share information inside listings. Defaults to 2.| +|=== diff --git a/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.md b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.md new file mode 100644 index 000000000..9303ac2a6 --- /dev/null +++ b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-added.md @@ -0,0 +1,233 @@ +# Added Variables between oCIS 4.0.0 and oCIS 5.0.0 + +| File | Variable | Description | Default | +|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------| +| ocis-pkg/shared/shared_types.go | `OCIS_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. | `false` | +| ocis-pkg/registry/registry.go | `MICRO_REGISTRY` (important change) | The Go micro registry type to use. Supported types are: 'memory', 'nats-js-kv' (default) and 'kubernetes'. Note that 'nats', 'etcd', 'consul' and 'mdns' are deprecated and will be removed in a later version. Only change on supervision of ownCloud Support. | `nats-js-kv` | +| ocis-pkg/natsjsregistry/registry.go | `MICRO_REGISTRY_AUTH_USERNAME` | Optional when using nats to authenticate with the nats cluster. | | +| | `MICRO_REGISTRY_AUTH_PASSWORD` | Optional when using nats to authenticate with the nats cluster. | | +| services/antivirus/pkg/config/config.go | `OCIS_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `ANTIVIRUS_ICAP_SCAN_TIMEOUT` | Scan timeout for the ICAP client. Defaults to '5m' (5 minutes). See the Environment Variable Types description for more details. | 5m0s | +| services/audit/pkg/config/config.go | `OCIS_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| services/auth-service/pkg/config/config.go | `OCIS_TRACING_ENABLED;AUTH_SERVICE_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;AUTH_SERVICE_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;AUTH_SERVICE_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;AUTH_SERVICE_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| | `OCIS_LOG_LEVEL;AUTH_SERVICE_LOG_LEVEL` | The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." | | +| | `OCIS_LOG_PRETTY;AUTH_SERVICE_LOG_PRETTY` | Activates pretty log output. | | +| | `OCIS_LOG_COLOR;AUTH_SERVICE_LOG_COLOR` | Activates colorized log output. | | +| | `OCIS_LOG_FILE;AUTH_SERVICE_LOG_FILE` | The path to the log file. Activates logging to this file if set. | | +| | `AUTH_SERVICE_DEBUG_ADDR` | Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed. | | +| | `AUTH_SERVICE_DEBUG_TOKEN` | Enables pprof, which can be used for profiling. | | +| | `AUTH_SERVICE_DEBUG_ZPAGES` | Enables zpages, which can be used for collecting and viewing in-memory traces. | | +| | `AUTH_SERVICE_GRPC_ADDR` | The bind address of the GRPC service. | | +| | `AUTH_SERVICE_GRPC_PROTOCOL` | The transport protocol of the GRPC service. | | +| | `OCIS_SERVICE_ACCOUNT_ID;AUTH_SERVICE_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;AUTH_SERVICE_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/auth-service/pkg/config/reva.go | `OCIS_JWT_SECRET;AUTH_SERVICE_JWT_SECRET` | The secret to mint and validate jwt tokens. | | +| services/clientlog/pkg/config/config.go | `OCIS_REVA_GATEWAY` | CS3 gateway used to look up user metadata | | +| | `OCIS_EVENTS_ENDPOINT;CLIENTLOG_EVENTS_ENDPOINT` | The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. | | +| | `OCIS_EVENTS_CLUSTER;CLIENTLOG_EVENTS_CLUSTER` | The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system. | | +| | `OCIS_INSECURE;CLIENTLOG_EVENTS_TLS_INSECURE` | Whether to verify the server TLS certificates. | | +| | `OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;CLIENTLOG_EVENTS_TLS_ROOT_CA_CERTIFICATE` | The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false. | | +| | `OCIS_EVENTS_ENABLE_TLS;CLIENTLOG_EVENTS_ENABLE_TLS` | Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_USERNAME;CLIENTLOG_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;CLIENTLOG_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_JWT_SECRET;CLIENTLOG_JWT_SECRET` | The secret to mint and validate jwt tokens. | | +| | `OCIS_SERVICE_ACCOUNT_ID;CLIENTLOG_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;CLIENTLOG_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/clientlog/pkg/config/debug.go | `CLIENTLOG_DEBUG_ADDR` | Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed. | | +| | `CLIENTLOG_DEBUG_TOKEN` | Token to secure the metrics endpoint. | | +| | `CLIENTLOG_DEBUG_PPROF` | Enables pprof, which can be used for profiling. | | +| | `CLIENTLOG_DEBUG_ZPAGES` | Enables zpages, which can be used for collecting and viewing in-memory traces. | | +| services/clientlog/pkg/config/log.go | `OCIS_LOG_LEVEL;CLIENTLOG_USERLOG_LOG_LEVEL` | The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." | | +| | `OCIS_LOG_PRETTY;CLIENTLOG_USERLOG_LOG_PRETTY` | Activates pretty log output. | | +| | `OCIS_LOG_COLOR;CLIENTLOG_USERLOG_LOG_COLOR` | Activates colorized log output. | | +| | `OCIS_LOG_FILE;CLIENTLOG_USERLOG_LOG_FILE` | The path to the log file. Activates logging to this file if set. | | +| services/clientlog/pkg/config/tracing.go | `OCIS_TRACING_ENABLED;CLIENTLOG_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;CLIENTLOG_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;CLIENTLOG_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;CLIENTLOG_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| services/eventhistory/pkg/config/config.go | `OCIS_PERSISTENT_STORE_AUTH_USERNAME;EVENTHISTORY_STORE_AUTH_USERNAME` | The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_PERSISTENT_STORE_AUTH_PASSWORD;EVENTHISTORY_STORE_AUTH_PASSWORD` | The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_EVENTS_AUTH_USERNAME;EVENTHISTORY_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;EVENTHISTORY_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| services/frontend/pkg/config/config.go | `OCIS_ENABLE_RESHARING` | Changing this value is NOT supported. Enables the support for resharing in the clients. | | +| | `FRONTEND_DEFAULT_LINK_PERMISSIONS` | Defines the default permissions a link is being created with. Possible values are 0 (= internal link, for instance members only) and 1 (= public link with viewer permissions). Defaults to 1. | | +| | `FRONTEND_AUTO_ACCEPT_SHARES` | Defines if shares should be auto accepted by default. Users can change this setting individually in their profile. | true | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;FRONTEND_OCS_STAT_CACHE_DISABLE_PERSISTENCE` | Disable persistence of the cache. Only applies when using the 'nats-js-kv' store type. Defaults to false. | false | +| | `FRONTEND_OCS_LIST_OCM_SHARES` | Include OCM shares when listing shares. See the OCM service documentation for more details. | | +| | `OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_SHARE_MUST_HAVE_PASSWORD` | Set this to true if you want to enforce passwords on all public shares. | | +| | `FRONTEND_OCS_INCLUDE_OCM_SHAREES` | nclude OCM sharees when listing sharees. | | +| | `OCIS_EVENTS_ENDPOINT;FRONTEND_EVENTS_ENDPOINT` | The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. | | +| | `OCIS_EVENTS_CLUSTER;FRONTEND_EVENTS_CLUSTER` | The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system. | | +| | `OCIS_INSECURE;FRONTEND_EVENTS_TLS_INSECURE` | Whether to verify the server TLS certificates. | | +| | `FRONTEND_EVENTS_TLS_ROOT_CA_CERTIFICATE;OCS_EVENTS_TLS_ROOT_CA_CERTIFICATE` | The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false. | | +| | `OCIS_EVENTS_ENABLE_TLS;FRONTEND_EVENTS_ENABLE_TLS` | Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_USERNAME;FRONTEND_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;FRONTEND_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_SERVICE_ACCOUNT_ID;FRONTEND_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;FRONTEND_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| | `OCIS_PASSWORD_POLICY_MIN_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS` | Define the minimum password length. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` | Define the minimum number of lowercase characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` | Define the minimum number of uppercase characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_DIGITS;FRONTEND_PASSWORD_POLICY_MIN_DIGITS` | Define the minimum number of digits. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` | Define the minimum number of special characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` | Path to the 'banned passwords list' file. See the documentation for more details. | | +| services/gateway/pkg/config/config.go | `GATEWAY_STORAGE_REGISTRY_DRIVER` | The driver name of the storage registry to use. | | +| | `GATEWAY_STORAGE_REGISTRY_RULES` | The rules for the storage registry. See the Environment Variable Types description for more details. | | +| | `GATEWAY_STORAGE_REGISTRY_CONFIG_JSON` | Additional configuration for the storage registry in json format. | | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_STAT_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the stat cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_PROVIDER_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the provider cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;GATEWAY_CREATE_HOME_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the create home cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| services/graph/pkg/config/cache.go | `OCIS_CACHE_DISABLE_PERSISTENCE;GRAPH_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_CACHE_AUTH_USERNAME;GRAPH_CACHE_AUTH_USERNAME` | The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_CACHE_AUTH_PASSWORD;GRAPH_CACHE_AUTH_PASSWORD` | The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| services/graph/pkg/config/config.go | `GRAPH_IDENTITY_SEARCH_MIN_LENGTH` | The minimum length the search term needs to have for unprivileged users when searching for users or groups. | | +| | `OCIS_EVENTS_AUTH_USERNAME;GRAPH_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;GRAPH_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_SERVICE_ACCOUNT_ID;GRAPH_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;GRAPH_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| | `OCIS_ENABLE_RESHARING;GRAPH_ENABLE_RESHARING` | Changing this value is NOT supported. Enables the support for resharing in the clients. | | +| services/notifications/pkg/config/config.go | `OCIS_DEFAULT_LANGUAGE` | The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details. | | +| | `OCIS_EVENTS_AUTH_USERNAME;NOTIFICATIONS_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;NOTIFICATIONS_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_SERVICE_ACCOUNT_ID;NOTIFICATIONS_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;NOTIFICATIONS_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/ocdav/pkg/config/config.go | `OCDAV_OCM_NAMESPACE` | The human readable path prefix for the ocm shares. | | +| services/ocm/pkg/config/config.go | `SHARING_OCM_PROVIDER_AUTHORIZER_DRIVER` | Driver to be used to persist ocm invites. Supported value is only 'json'. | `json` | +| | `OCM_HTTP_ADDR` | The bind address of the HTTP service. | | +| | `OCM_HTTP_PROTOCOL` | The transport protocol of the HTTP service. | | +| | `OCM_HTTP_PREFIX` | The path prefix where OCM can be accessed (defaults to /). | | +| | `OCIS_SERVICE_ACCOUNT_ID;OCM_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;OCM_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| | `OCIS_CORS_ALLOW_ORIGINS;OCM_CORS_ALLOW_ORIGINS` | A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_METHODS;OCM_CORS_ALLOW_METHODS` | A list of allowed CORS methods. See following chapter for more details: *Access-Control-Allow-Methods* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_HEADERS;OCM_CORS_ALLOW_HEADERS` | A list of allowed CORS headers. See following chapter for more details: *Access-Control-Allow-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_CREDENTIALS;OCM_CORS_ALLOW_CREDENTIALS` | Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials. | | +| | `OCM_GRPC_ADDR` | The bind address of the GRPC service. | | +| | `OCM_GRPC_PROTOCOL` | The transport protocol of the GRPC service. | | +| | `OCM_SCIENCEMESH_PREFIX` | URL path prefix for the ScienceMesh service. Note that the string must not start with '/'. | | +| | `OCM_MESH_DIRECTORY_URL ` | URL of the mesh directory service. | | +| | `OCM_OCMD_PREFIX` | URL path prefix for the OCMd service. Note that the string must not start with '/'. | | +| | `OCM_OCMD_EXPOSE_RECIPIENT_DISPLAY_NAME` | Expose the display name of OCM share recipients. | | +| | `OCM_OCM_INVITE_MANAGER_DRIVER` | Driver to be used to persist ocm invites. Supported value is only 'json'. | `json` | +| | `OCM_OCM_INVITE_MANAGER_INSECURE` | Disable TLS certificate validation for the OCM connections. Do not set this in production environments. | | +| | `OCM_OCM_INVITE_MANAGER_JSON_FILE` | Path to the JSON file where OCM invite data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage. | | +| | `OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE` | Path to the JSON file where ocm invite data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage. | | +| | `OCM_OCM_PROVIDER_AUTHORIZER_VERIFY_REQUEST_HOSTNAME` | Verify the hostname of the request against the hostname of the OCM provider. | | +| | `OCM_OCM_CORE_DRIVER` | Driver to be used to persist ocm shares. Supported value is only 'json'. | `json` | +| | `OCM_OCM_STORAGE_PROVIDER_INSECURE` | Disable TLS certificate validation for the OCM connections. Do not set this in production environments. | | +| | `OCM_OCM_STORAGE_PROVIDER_STORAGE_ROOT` | Directory where the ocm storage provider persists its data like tus upload info files. | | +| | `OCM_OCM_CORE_JSON_FILE` | Path to the JSON file where OCM share data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage. | | +| | `OCM_OCM_SHARE_PROVIDER_DRIVER` | Driver to be used to persist ocm shares. Supported value is only 'json'. | `json` | +| | `OCM_OCM_SHARE_PROVIDER_INSECURE` | Disable TLS certificate validation for the OCM connections. Do not set this in production environments. | | +| | `OCM_WEBAPP_TEMPLATE` | 'Template for the webapp url. | | +| | `OCM_OCM_SHAREPROVIDER_JSON_FILE` | Path to the JSON file where OCM share data will be stored. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/storage. | | +| services/ocm/pkg/config/debug.go | `OCM_DEBUG_ADDR` | Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed. | | +| | `OCM_DEBUG_TOKEN` | Token to secure the metrics endpoint. | | +| | `OCM_DEBUG_PPROF` | Enables pprof, which can be used for profiling. | | +| | `OCM_DEBUG_ZPAGES` | Enables zpages, which can be used for collecting and viewing in-memory traces. | | +| services/ocm/pkg/config/log.go | `OCIS_LOG_LEVEL;OCM_LOG_LEVEL` | The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." | | +| | `OCIS_LOG_PRETTY;OCM_LOG_PRETTY` | Activates pretty log output. | | +| | `OCIS_LOG_COLOR;OCM_LOG_COLOR` | Activates colorized log output. | | +| | `OCIS_LOG_FILE;OCM_LOG_FILE` | The path to the log file. Activates logging to this file if set. | | +| services/ocm/pkg/config/tracing.go | `OCIS_TRACING_ENABLED;OCM_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;OCM_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;OCM_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;OCM_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| services/policies/pkg/config/config.go | `OCIS_EVENTS_AUTH_USERNAME;POLICIES_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;POLICIES_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| services/policies/pkg/config/tracing.go | `OCIS_TRACING_ENABLED;POLICIES_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;POLICIES_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;POLICIES_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;POLICIES_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| services/postprocessing/pkg/config/config.go | `POSTPROCESSING_RETRY_BACKOFF_DURATION` | The base for the exponential backoff duration before retrying a failed postprocessing step. See the Environment Variable Types description for more details. | | +| | `POSTPROCESSING_MAX_RETRIES` | The maximum number of retries for a failed postprocessing step. | | +| | `OCIS_EVENTS_AUTH_USERNAME;POSTPROCESSING_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;POSTPROCESSING_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_PERSISTENT_STORE_AUTH_USERNAME;POSTPROCESSING_STORE_AUTH_USERNAME` | The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_PERSISTENT_STORE_AUTH_PASSWORD;POSTPROCESSING_STORE_AUTH_PASSWORD` | The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| services/postprocessing/pkg/config/tracing.go | `OCIS_TRACING_ENABLED;POSTPROCESSING_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;POSTPROCESSING_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;POSTPROCESSING_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;POSTPROCESSING_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| services/proxy/pkg/config/config.go | `OCIS_CACHE_AUTH_USERNAME;PROXY_OIDC_USERINFO_CACHE_AUTH_USERNAME` | The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_CACHE_AUTH_PASSWORD;PROXY_OIDC_USERINFO_CACHE_AUTH_PASSWORD` | The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_SERVICE_ACCOUNT_ID;PROXY_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;PROXY_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/search/pkg/config/config.go | `OCIS_SERVICE_ACCOUNT_ID;SEARCH_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;SEARCH_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/search/pkg/config/content.go | `SEARCH_EXTRACTOR_TIKA_CLEAN_STOP_WORDS` | Defines if stop words should be cleaned or not. See the documentation for more details. | | +| services/search/pkg/config/search.go | `OCIS_EVENTS_AUTH_USERNAME;SEARCH_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;SEARCH_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| services/settings/pkg/config/config.go | `SETTINGS_SERVICE_ACCOUNT_IDS;OCIS_SERVICE_ACCOUNT_ID` | The list of all service account IDs. These will be assigned the hidden 'service-account' role. Note: When using 'OCIS_SERVICE_ACCOUNT_ID' this will contain only one value while 'SETTINGS_SERVICE_ACCOUNT_IDS' can have multiple. See the 'auth-service' service description for more details about service accounts. | | +| | `OCIS_DEFAULT_LANGUAGE` | The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details. | | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;SETTINGS_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | | +| | `OCIS_EVENTS_AUTH_USERNAME;SETTINGS_EVENTS_AUTH_USERNAME` | The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;SETTINGS_EVENTS_AUTH_PASSWORD` | The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured. | | +| services/sharing/pkg/config/config.go | `OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD` | Set this to true if you want to enforce passwords on all public shares. | | +| | `OCIS_PASSWORD_POLICY_MIN_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_CHARACTERS` | Define the minimum password length. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS` | Define the minimum number of lowercase characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS` | Define the minimum number of uppercase characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_DIGITS;SHARING_PASSWORD_POLICY_MIN_DIGITS` | Define the minimum number of digits. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS` | Define the minimum number of special characters. Defaults to 0 if not set. | 0 | +| | `OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;SHARING_PASSWORD_POLICY_BANNED_PASSWORDS_LIST` | Path to the 'banned passwords list' file. See the documentation for more details. | | +| services/sse/pkg/config/config.go | `OCIS_LOG_LEVEL;SSE_LOG_LEVEL` | The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." | | +| | `OCIS_LOG_PRETTY;SSE_LOG_PRETTY` | Activates pretty log output. | | +| | `OCIS_LOG_COLOR;SSE_LOG_COLOR` | Activates colorized log output. | | +| | `OCIS_LOG_FILE;SSE_LOG_FILE` | The path to the log file. Activates logging to this file if set. | | +| | `SSE_DEBUG_ADDR` | Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed. | | +| | `SSE_DEBUG_TOKEN` | Token to secure the metrics endpoint. | | +| | `SSE_DEBUG_PPROF` | Enables pprof, which can be used for profiling. | | +| | `SSE_DEBUG_ZPAGES` | Enables zpages, which can be used for collecting and viewing in-memory traces. | | +| | `OCIS_EVENTS_ENDPOINT;SSE_EVENTS_ENDPOINT` | The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. | | +| | `OCIS_EVENTS_CLUSTER;SSE_EVENTS_CLUSTER` | The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system. | | +| | `OCIS_INSECURE;SSE_EVENTS_TLS_INSECURE` | Whether to verify the server TLS certificates. | | +| | `OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SSE_EVENTS_TLS_ROOT_CA_CERTIFICATE` | The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false. | | +| | `OCIS_EVENTS_ENABLE_TLS;SSE_EVENTS_ENABLE_TLS` | Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_USERNAME;SSE_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;SSE_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.. | | +| | `OCIS_CORS_ALLOW_ORIGINS;SSE_CORS_ALLOW_ORIGINS` | A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_METHODS;SSE_CORS_ALLOW_METHODS` | A list of allowed CORS methods. See following chapter for more details: *Access-Control-Allow-Methods* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_HEADERS;SSE_CORS_ALLOW_HEADERS` | A list of allowed CORS headers. See following chapter for more details: *Access-Control-Allow-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers. See the Environment Variable Types description for more details. | | +| | `OCIS_CORS_ALLOW_CREDENTIALS;SSE_CORS_ALLOW_CREDENTIALS` | Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials. | | +| | `SSE_HTTP_ADDR` | | The bind address of the HTTP service. | | +| | `SSE_HTTP_ROOT` | | The root path of the HTTP service. | | +| | `OCIS_JWT_SECRET;SSE_JWT_SECRET` | The secret to mint and validate jwt tokens. | | +| services/sse/pkg/config/tracing.go | `OCIS_TRACING_ENABLED;SSE_TRACING_ENABLED` | Activates tracing. | | +| | `OCIS_TRACING_TYPE;SSE_TRACING_TYPE` | The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."` | | +| | `OCIS_TRACING_ENDPOINT;SSE_TRACING_ENDPOINT` | The endpoint of the tracing agent. | | +| | `OCIS_TRACING_COLLECTOR;SSE_TRACING_COLLECTOR` | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset. | | +| services/storage-system/pkg/config/config.go | `OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_SYSTEM_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| services/storage-users/pkg/config/config.go | `OCIS_GATEWAY_GRPC_ADDR;STORAGE_USERS_GATEWAY_GRPC_ADDR` | The bind address of the gateway GRPC address. | | +| | `OCIS_MACHINE_AUTH_API_KEY` | Machine auth API key used to validate internal requests necessary for the access to resources from other services. | | +| | `STORAGE_USERS_CLI_MAX_ATTEMPTS_RENAME_FILE` | The maximum number of attempts to rename a file when a user restores a file to an existing destination with the same name. The minimum value is 100. | | +| | `OCIS_EVENTS_AUTH_USERNAME;STORAGE_USERS_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;STORAGE_USERS_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_STAT_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the stat cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_FILEMETADATA_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the file metadata cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_CACHE_DISABLE_PERSISTENCE;STORAGE_USERS_ID_CACHE_DISABLE_PERSISTENCE` | Disables persistence of the id cache. Only applies when store type 'nats-js-kv' is configured. Defaults to false. | false | +| | `OCIS_SERVICE_ACCOUNT_ID;STORAGE_USERS_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;STORAGE_USERS_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/userlog/pkg/config/config.go | `OCIS_DEFAULT_LANGUAGE` | The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details. | | +| | `OCIS_PERSISTENT_STORE_AUTH_USERNAME;USERLOG_STORE_AUTH_USERNAME` | The username to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_PERSISTENT_STORE_AUTH_PASSWORD;USERLOG_STORE_AUTH_PASSWORD` | The password to authenticate with the store. Only applies when store type 'nats-js-kv' is configured. | | +| | `OCIS_EVENTS_AUTH_USERNAME;USERLOG_EVENTS_AUTH_USERNAME` | The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_EVENTS_AUTH_PASSWORD;USERLOG_EVENTS_AUTH_PASSWORD` | The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services. | | +| | `OCIS_SERVICE_ACCOUNT_ID;USERLOG_SERVICE_ACCOUNT_ID` | The ID of the service account the service should use. See the 'auth-service' service description for more details. | | +| | `OCIS_SERVICE_ACCOUNT_SECRET;USERLOG_SERVICE_ACCOUNT_SECRET` | The service account secret. | | +| services/web/pkg/config/options.go | `WEB_OPTION_LOGIN_URL`Specifies the target URL to the login page. This is helpful when an external IdP is used. This option is disabled by default. Example URL like: https://www.myidp.com/login. | | | +| | `WEB_OPTION_DISABLED_EXTENSIONS` | A list to disable specific Web extensions identified by their ID. The ID can e.g. be taken from the 'index.ts' file of the web extension. Example: 'com.github.owncloud.web.files.search,com.github.owncloud.web.files.print'. See the Environment Variable Types description for more details. | | +| | `WEB_OPTION_USER_LIST_REQUIRES_FILTER` | Defines whether one or more filters must be set in order to list users in the Web admin settings. Set this option to 'true' if running in an environment with a lot of users and listing all users could slow down performance. Defaults to 'false'. | false | +| | `WEB_OPTION_EMBED_ENABLED` | Defines whether Web should be running in 'embed' mode. Setting this to 'true' will enable a stripped down version of Web with reduced functionality used to integrate Web into other applications like via iFrame. Setting it to 'false' or not setting it (default) will run Web as usual with all functionality enabled. See the text description for more details. | | +| | `WEB_OPTION_EMBED_TARGET` | Defines how Web is being integrated when running in 'embed' mode. Currently, the only supported options are '' (empty) and 'location'. With '' which is the default, Web will run regular as defined via the 'embed.enabled' config option. With 'location', Web will run embedded as location picker. Resource selection will be disabled and the selected resources array always includes the current folder as the only item. See the text description for more details. | | +| | `WEB_OPTION_EMBED_MESSAGES_ORIGIN` | Defines a URL under which Web can be integrated via iFrame in 'embed' mode. Note that setting this is mandatory when running Web in 'embed' mode. Use '*' as value to allow running the iFrame under any URL, although this is not recommended for security reasons. See the text description for more details. | | +| | `WEB_OPTION_EMBED_DELEGATE_AUTHENTICATION` | Defines whether Web should require authentication to be done by the parent application when running in 'embed' mode. If set to 'true' Web will not try to authenticate the user on its own but will require an access token coming from the parent application. Defaults to being unset. | | +| | `WEB_OPTION_EMBED_DELEGATE_AUTHENTICATION_ORIGIN` | Defines the host to validate the message event origin against when running Web in 'embed' mode with delegated authentication. Defaults to event message origin validation being omitted, which is only recommended for development setups. | | +| | `WEB_OPTION_CONCURRENT_REQUESTS_RESOURCE_BATCH_ACTIONS` | Defines the maximum number of concurrent requests per file/folder/space batch action. Defaults to 4. | | +| | `WEB_OPTION_CONCURRENT_REQUESTS_SSE` | Defines the maximum number of concurrent requests in SSE event handlers. Defaults to 4. | | +| | `WEB_OPTION_CONCURRENT_REQUESTS_SHARES_CREATE` | Defines the maximum number of concurrent requests per sharing invite batch. Defaults to 4. | | +| | `WEB_OPTION_CONCURRENT_REQUESTS_SHARES_LIST` | Defines the maximum number of concurrent requests when loading individual share information inside listings. Defaults to 2. | | diff --git a/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.adoc b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.adoc new file mode 100644 index 000000000..34120a5ae --- /dev/null +++ b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.adoc @@ -0,0 +1,55 @@ +// Removed Variables between oCIS 4.0.0 and oCIS 5.0.0 +// commenting the headline to make it better includable + +// table created based on the .md file per 2024.02.12 +// the table should be recreated/updated based on the final .md file + +[width="100%",cols="~,~,~,~",options="header"] +|=== +| Service| Variable| Replacement| Description + +| xref:{s-path}/auth-basic.adoc[auth-basic] +| `LDAP_BIND_PASSWORD` +| `OCIS_LDAP_BIND_PASSWORD` +| Password to use for authenticating the 'bind_dn'. + +| xref:{s-path}/graph.adoc[graph] +| `LDAP_BIND_PASSWORD` +| `OCIS_LDAP_BIND_PASSWORD` +| Password to use for authenticating the 'bind_dn'. + +| xref:{s-path}/groups.adoc[groups] +| `LDAP_BIND_PASSWORD` +| `OCIS_LDAP_BIND_PASSWORD` +| Password to use for authenticating the 'bind_dn'. + +| xref:{s-path}/idp.adoc[idp] +| `LDAP_BIND_PASSWORD` +| `OCIS_LDAP_BIND_PASSWORD` +| Password to use for authenticating the 'bind_dn'. + +| xref:{s-path}/sharing.adoc[sharing] +| `SHARING_EVENTS_TLS_ROOT_CA_CERT` +| `SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE` +| The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false. + +| xref:{s-path}/users.adoc[users] +| `LDAP_BIND_PASSWORD` +| `OCIS_LDAP_BIND_PASSWORD` +| Password to use for authenticating the 'bind_dn'. + +| +| `LDAP_USER_SCHEMA_ID_IS_OCTETSTRING` +| `OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING` +| Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is e.g. required when using the 'objectGUID' attribute of Active Directory for the user ID's. + +| xref:{s-path}/web.adoc[web] +| `WEB_OPTION_IMPRINT_URL` +| To be configured via the `theme.json` file +| Specifies the target URL for the imprint link valid for the ocis instance in the account menu. + +| +| `WEB_OPTION_PRIVACY_URL` +| To be configured via the `theme.json` file +| Specifies the target URL for the privacy link valid for the ocis instance in the account menu. +|=== diff --git a/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.md b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.md new file mode 100644 index 000000000..f20c695c0 --- /dev/null +++ b/docs/services/general-info/env-var-deltas/4.0.0-5.0.0-removed.md @@ -0,0 +1,13 @@ +# Removed Variables between oCIS 4.0.0 and oCIS 5.0.0 + +| File | Variable | Replacement | Description | +|------------------------------------------|--------------------------------------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| services/auth-basic/pkg/config/config.go | `LDAP_BIND_PASSWORD` | `OCIS_LDAP_BIND_PASSWORD` | Password to use for authenticating the 'bind_dn'. | +| services/graph/pkg/config/config.go | `LDAP_BIND_PASSWORD` | `OCIS_LDAP_BIND_PASSWORD` | Password to use for authenticating the 'bind_dn'. | +| services/groups/pkg/config/config.go | `LDAP_BIND_PASSWORD` | `OCIS_LDAP_BIND_PASSWORD` | Password to use for authenticating the 'bind_dn'. | +| services/idp/pkg/config/config.go | `LDAP_BIND_PASSWORD` | `OCIS_LDAP_BIND_PASSWORD` | Password to use for authenticating the 'bind_dn'. | +| services/sharing/pkg/config/config.go | `SHARING_EVENTS_TLS_ROOT_CA_CERT` | `SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE` | The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false. | +| services/users/pkg/config/config.go | `LDAP_BIND_PASSWORD` | `OCIS_LDAP_BIND_PASSWORD` | Password to use for authenticating the 'bind_dn'. | +| | `LDAP_USER_SCHEMA_ID_IS_OCTETSTRING` | `OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING` | Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is e.g. required when using the 'objectGUID' attribute of Active Directory for the user ID's. | +| services/web/pkg/config/options.go | `WEB_OPTION_IMPRINT_URL` | To be configured via the `theme.json` file | Specifies the target URL for the imprint link valid for the ocis instance in the account menu. | +| | `WEB_OPTION_PRIVACY_URL` | To be configured via the `theme.json` file | Specifies the target URL for the privacy link valid for the ocis instance in the account menu. | diff --git a/docs/services/general-info/env-var-deltas/make-adoc-table-from-md.txt b/docs/services/general-info/env-var-deltas/make-adoc-table-from-md.txt new file mode 100644 index 000000000..f2f256ea6 --- /dev/null +++ b/docs/services/general-info/env-var-deltas/make-adoc-table-from-md.txt @@ -0,0 +1,46 @@ +make the markdown a asciidoc table + +cp 4.0.0-5.0.0.md 4.0.0-5.0.0.adoc copy file +sed -i 's/.$//g' 4.0.0-5.0.0.adoc remove last character of each line which is | +sed -i 's/ \+//g' 4.0.0-5.0.0.adoc remove any double spaces +fully remove the line containing |----...| +rename: | File| Variable| Description| Default --> | Service| Variable| Description| Default + +add on top under the page heading: +[width="100%",cols="~,~,~,~",options="header"] +|=== + +and at the bottom +|=== + +check SSE_HTTP_ADDR and SSE_HTTP_ROOT, there is one | too much +check WEB_OPTION_LOGIN_URL, there is one | missing +check WEB_OPTION_DISABLED_EXTENSIONS, in the line before, there is one | too much + + +sed -i "s/\;/\` \+\n\`/g" 4.0.0-5.0.0.adoc make each envvar a single line for ease of readability +sed -i "s/\`|/\`\n|/g" 4.0.0-5.0.0.adoc put the next | after the last envvar in a line into a new line +sed -i "s/|| \`/|\n|\`/g" 4.0.0-5.0.0.adoc split the || before an envvar into two lines +sed -i "s/services\/(.*)\//xref\:\{s-path\}\/\1\.adoc/g" 4.0.0-5.0.0.adoc make "service" a xref + +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/config.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/reva.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/debug.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/log.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/tracing.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/cache.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/content.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/search.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc +sed -i "s/\(services\/\)\(.*\)\(\/pkg\/config\/options.go\)/xref\:\{s-path\}\/\2.adoc\[\2\]/g" 4.0.0-5.0.0.adoc + services/antivirus/pkg/config/config.go --> xref:{s-path}/antivirus.adoc[antivirus] + +change: ocis-pkg/shared/shared_types.go --> Available in multiple services + +sed -i "s/\]|/\]\n|/g" 4.0.0-5.0.0.adoc put tab separator of service and envvar into a new line +sed -i "s/|\`/| \`/g" 4.0.0-5.0.0.adoc |`OCIS --> | `OCIS + +sed -i 's/\.\./\./g' 4.0.0-5.0.0.adoc remove trailing double dots in sentences + +manually fix (remove) any double occurrences if a service like clientlog, auth-service, graph, ocm, policies, postprocessing, search, sse. this is because the origin path points to different go routines inside the same service. + +manually checking the result and fixing issues (mostly minor) is mandatory diff --git a/docs/services/general-info/new-service-checklist.md b/docs/services/general-info/new-service-checklist.md index dd50d3f1e..53a908f81 100644 --- a/docs/services/general-info/new-service-checklist.md +++ b/docs/services/general-info/new-service-checklist.md @@ -39,6 +39,6 @@ Use this checklist with copy/paste in your PR - right from the beginning. It ren - [ ] If new envvars are introduced which serve the same purpose but in multiple services, an additional envvar must be added at the beginning of the list starting with `OCIS_` (global envvar). - [ ] Ensure that a service has a debug port - [ ] If the new service introduces a new port: - - the port must be added to [port-ranges.md](https://github.com/owncloud/ocis/blob/master/docs/services/port-ranges.md) and to the readme.md file. + - the port must be added to [port-ranges.md](https://github.com/owncloud/ocis/blob/master/docs/services/general-info/port-ranges.md) and to the readme.md file. - [ ] Make sure to have a function `FullDefaultConfig()` in `pkg/config/defaults/defaultconfig.go` of your service. It is needed to create the documentation. ``` diff --git a/docs/templates/ADOC_extended.tmpl b/docs/templates/ADOC_extended.tmpl index 16c558f0d..829570039 100644 --- a/docs/templates/ADOC_extended.tmpl +++ b/docs/templates/ADOC_extended.tmpl @@ -2,13 +2,12 @@ [caption=] .Environment variables with extended scope not included in a service -[width="100%",cols="~,~,~,~",options="header"] +[width="100%",cols="35%,10%,18%,~"",options="header"] |=== | Name | Type | Default Value | Description - {{- range .Variables}} {{- if .Ignore }} diff --git a/docs/templates/ADOC_global.tmpl b/docs/templates/ADOC_global.tmpl index 5c9d02a24..424c01048 100644 --- a/docs/templates/ADOC_global.tmpl +++ b/docs/templates/ADOC_global.tmpl @@ -1,12 +1,5 @@ // collected through docs/helpers/adoc-generator.go.tmpl -Note that some global environment variables have been deprecated and replaced by a new one starting with `OCIS_` for naming consistency: - -* All envvars starting with `LDAP_` -* All envvars starting with `IDM_` except `IDM_CREATE_DEMO_USERS` -* The following envvars: `OCIS_REVA_GATEWAY`, `STORAGE_TRANSFER_SECRET`, `OCIS_ASYNC_UPLOADS`, `USERLOG_MACHINE_AUTH_API_KEY`. -* Note that `WEB_UI_CONFIG_FILE` is not a global envar and will dropped from the list in a later release. - [.landscape] [caption=] .Environment variables with global scope available in multiple services @@ -17,9 +10,7 @@ Note that some global environment variables have been deprecated and replaced by | Type | Default Value | Description - {{ range . }} - a| `{{ .Name }}` a| [subs=attributes+] diff --git a/go.mod b/go.mod index c14c2fb16..eaff57a69 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,10 @@ require ( github.com/Nerzal/gocloak/v13 v13.9.0 github.com/bbalet/stopwords v1.0.0 github.com/blevesearch/bleve/v2 v2.3.10 + github.com/cenkalti/backoff v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.9.0 github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 - github.com/cs3org/reva/v2 v2.18.1-0.20240129131717-cff0a2eeb959 + github.com/cs3org/reva/v2 v2.18.1-0.20240208105019-d5e420d294be github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 github.com/disintegration/imaging v1.6.2 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e @@ -39,7 +40,7 @@ require ( github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0 github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 - github.com/go-playground/validator/v10 v10.17.0 + github.com/go-playground/validator/v10 v10.18.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.3 @@ -59,7 +60,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/mna/pigeon v1.2.1 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/nats-io/nats-server/v2 v2.10.9 + github.com/nats-io/nats-server/v2 v2.10.10 github.com/nats-io/nats.go v1.32.0 github.com/oklog/run v1.1.0 github.com/olekukonko/tablewriter v0.0.5 @@ -75,7 +76,7 @@ require ( github.com/r3labs/sse/v2 v2.10.0 github.com/riandyrn/otelchi v0.5.1 github.com/rogpeppe/go-internal v1.12.0 - github.com/rs/zerolog v1.31.0 + github.com/rs/zerolog v1.32.0 github.com/shamaton/msgpack/v2 v2.1.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 @@ -87,22 +88,22 @@ require ( github.com/xhit/go-simple-mail/v2 v2.16.0 go-micro.dev/v4 v4.9.0 go.etcd.io/bbolt v1.3.8 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 - go.opentelemetry.io/contrib/zpages v0.47.0 - go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 + go.opentelemetry.io/contrib/zpages v0.48.0 + go.opentelemetry.io/otel v1.23.1 go.opentelemetry.io/otel/exporters/jaeger v1.17.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 - go.opentelemetry.io/otel/sdk v1.22.0 - go.opentelemetry.io/otel/trace v1.22.0 - golang.org/x/crypto v0.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 + go.opentelemetry.io/otel/sdk v1.23.1 + go.opentelemetry.io/otel/trace v1.23.1 + golang.org/x/crypto v0.19.0 golang.org/x/image v0.15.0 - golang.org/x/net v0.20.0 - golang.org/x/oauth2 v0.16.0 + golang.org/x/net v0.21.0 + golang.org/x/oauth2 v0.17.0 golang.org/x/sync v0.6.0 - golang.org/x/term v0.16.0 + golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe - google.golang.org/grpc v1.61.0 + google.golang.org/grpc v1.61.1 google.golang.org/protobuf v1.32.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 @@ -152,7 +153,6 @@ require ( github.com/bluele/gcache v0.0.2 // indirect github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f // indirect github.com/bombsimon/logrusr/v3 v3.1.0 // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/ceph/go-ceph v0.18.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -249,9 +249,9 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/juliangruber/go-intersect v1.1.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.5 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/libregraph/oidc-go v1.0.0 // indirect github.com/longsleep/go-metrics v1.0.0 // indirect github.com/longsleep/rndm v1.2.0 // indirect @@ -329,15 +329,15 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib v1.0.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 // indirect + go.opentelemetry.io/otel/metric v1.23.1 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.16.1 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/go.sum b/go.sum index 3446ba9ad..5141a427f 100644 --- a/go.sum +++ b/go.sum @@ -1019,8 +1019,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY= github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.18.1-0.20240129131717-cff0a2eeb959 h1:8uiYRWlbrhQJk4pHawpJUTFx/Yy0G5yvMYam2TMKDYo= -github.com/cs3org/reva/v2 v2.18.1-0.20240129131717-cff0a2eeb959/go.mod h1:GCN3g6uYE0Nvd31dGlhaGGyUviUfbG2NkecPRv5oSc4= +github.com/cs3org/reva/v2 v2.18.1-0.20240208105019-d5e420d294be h1:vljLn7q9NPfiynhzGBRD1sv8qhYGd6SZOHgvzv+Q9iA= +github.com/cs3org/reva/v2 v2.18.1-0.20240208105019-d5e420d294be/go.mod h1:GCN3g6uYE0Nvd31dGlhaGGyUviUfbG2NkecPRv5oSc4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -1236,8 +1236,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= -github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= @@ -1588,8 +1588,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E= +github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1617,8 +1617,8 @@ github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HK github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 h1:k56sFOOJ0CYuQtGoRSeAMhP1R692+iNH+S1dC/CEz0w= github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= github.com/libregraph/idm v0.4.1-0.20231213140724-56a222fb4215 h1:Yw/I6l/0S/zDq2Hnibvwy8cVLpMaBwDe0aUSv/FNU6U= @@ -1745,8 +1745,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= -github.com/nats-io/nats-server/v2 v2.10.9 h1:VEW43Zz+p+9lARtiPM9ctd6ckun+92ZT2T17HWtwiFI= -github.com/nats-io/nats-server/v2 v2.10.9/go.mod h1:oorGiV9j3BOLLO3ejQe+U7pfAGyPo+ppD7rpgNF6KTQ= +github.com/nats-io/nats-server/v2 v2.10.10 h1:g1Wd64J5SGsoqWSx1qoNu9/At7a2x+jE7Qtf2XpEx/I= +github.com/nats-io/nats-server/v2 v2.10.10/go.mod h1:/TE61Dos8NlwZnjzyE3ZlOnM6dgl7tf937dnf4VclrA= github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0= github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= @@ -1915,8 +1915,8 @@ 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 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= 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/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -2094,32 +2094,32 @@ go.opentelemetry.io/contrib v1.0.0 h1:khwDCxdSspjOLmFnvMuSHd/5rPzbTx0+l6aURwtQdf go.opentelemetry.io/contrib v1.0.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/contrib/zpages v0.47.0 h1:ekpdNa2wqOvAfwZIGDIIV02zmR+z08aWPt21KrPJnaU= -go.opentelemetry.io/contrib/zpages v0.47.0/go.mod h1:rBeFA/UxnMjRlEGpmClIqzf1mCIKtl7ahjww3wsSdGs= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= +go.opentelemetry.io/contrib/zpages v0.48.0 h1:7vnqarYRu2VYvUUwrLWXRk5HqPuCbtuZdgR4eaVeP3Y= +go.opentelemetry.io/contrib/zpages v0.48.0/go.mod h1:fzewgBzh126TFUgnNhI9/1hjvnZes7wBKXAMQmyM01g= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= +go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= -go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 h1:o8iWeVFa1BcLtVEV0LzrCxV2/55tB3xLxADr6Kyoey4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1/go.mod h1:SEVfdK4IoBnbT2FXNM/k8yC08MrfbhWk3U4ljM8B3HE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 h1:p3A5+f5l9e/kuEBwLOrnpkIDHQFlHmbiVxMURWRK6gQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1/go.mod h1:OClrnXUjBqQbInvjJFjYSnMxBSCXBF8r3b34WqjiIrQ= +go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= +go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= +go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= +go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= 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.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= 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.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -2169,8 +2169,8 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 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= @@ -2315,8 +2315,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.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 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= @@ -2349,8 +2349,8 @@ 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/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= 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= @@ -2498,8 +2498,8 @@ 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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2516,8 +2516,8 @@ 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/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 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= @@ -2935,8 +2935,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.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= 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-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU= google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU= diff --git a/ocis-pkg/config/helpers.go b/ocis-pkg/config/helpers.go index 7b72333e9..c0ec6f1f4 100644 --- a/ocis-pkg/config/helpers.go +++ b/ocis-pkg/config/helpers.go @@ -20,6 +20,7 @@ var ( func BindSourcesToStructs(service string, dst interface{}) (*gofig.Config, error) { cnf := gofig.NewWithOptions(service) cnf.WithOptions(func(options *gofig.Options) { + options.ParseEnv = true options.DecoderConfig.TagName = decoderConfigTagName }) cnf.AddDriver(gooyaml.Driver) diff --git a/ocis-pkg/oidc/mocks/oidc_client.go b/ocis-pkg/oidc/mocks/oidc_client.go index 5a8223973..0aa0dfdfe 100644 --- a/ocis-pkg/oidc/mocks/oidc_client.go +++ b/ocis-pkg/oidc/mocks/oidc_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/ocis/pkg/command/version.go b/ocis/pkg/command/version.go index 3e251f953..e4c351b2f 100644 --- a/ocis/pkg/command/version.go +++ b/ocis/pkg/command/version.go @@ -5,29 +5,45 @@ import ( "os" tw "github.com/olekukonko/tablewriter" + "github.com/urfave/cli/v2" + mreg "go-micro.dev/v4/registry" + "github.com/owncloud/ocis/v2/ocis-pkg/config" "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/version" "github.com/owncloud/ocis/v2/ocis/pkg/register" - "github.com/urfave/cli/v2" - mreg "go-micro.dev/v4/registry" +) + +const ( + _skipServiceListingFlagName = "skip-services" ) // VersionCommand is the entrypoint for the version command. func VersionCommand(cfg *config.Config) *cli.Command { return &cli.Command{ - Name: "version", - Usage: "print the version of this binary and all running service instances", + Name: "version", + Usage: "print the version of this binary and all running service instances", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: _skipServiceListingFlagName, + Usage: "skip service listing", + }, + }, Category: "info", Action: func(c *cli.Context) error { fmt.Println("Version: " + version.GetString()) fmt.Printf("Compiled: %s\n", version.Compiled()) - fmt.Println("") + + if c.Bool(_skipServiceListingFlagName) { + return nil + } + + fmt.Print("\n") reg := registry.GetRegistry() serviceList, err := reg.ListServices() if err != nil { - fmt.Println(fmt.Errorf("could not list services: %v", err)) + fmt.Printf("could not list services: %v\n", err) return err } @@ -35,7 +51,7 @@ func VersionCommand(cfg *config.Config) *cli.Command { for _, s := range serviceList { s, err := reg.GetService(s.Name) if err != nil { - fmt.Println(fmt.Errorf("could not get service: %v", err)) + fmt.Printf("could not get service: %v\n", err) return err } services = append(services, s...) diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go index c1ef41747..7830bcde6 100644 --- a/ocis/pkg/runtime/service/service.go +++ b/ocis/pkg/runtime/service/service.go @@ -13,12 +13,17 @@ import ( "syscall" "time" + "github.com/cenkalti/backoff" + "github.com/cs3org/reva/v2/pkg/events/stream" + "github.com/cs3org/reva/v2/pkg/logger" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/mohae/deepcopy" "github.com/olekukonko/tablewriter" "github.com/thejerf/suture/v4" ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config" "github.com/owncloud/ocis/v2/ocis-pkg/log" + ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/shared" antivirus "github.com/owncloud/ocis/v2/services/antivirus/pkg/command" appProvider "github.com/owncloud/ocis/v2/services/app-provider/pkg/command" @@ -64,22 +69,19 @@ import ( var ( // runset keeps track of which services to start supervised. runset map[string]struct{} - // time to wait after starting the preliminary services - _preliminaryDelay = 6 * time.Second - // time to wait between starting service groups (preliminary, main, delayed) - _startDelay = 2 * time.Second + + // wait funcs run after the service group has been started. + _waitFuncs = []func(*ociscfg.Config) error{pingNats, pingGateway, nil, wait(time.Second), nil} ) type serviceFuncMap map[string]func(*ociscfg.Config) suture.Service // Service represents a RPC service. type Service struct { - Supervisor *suture.Supervisor - Preliminary serviceFuncMap - ServicesRegistry serviceFuncMap - Delayed serviceFuncMap - Additional serviceFuncMap - Log log.Logger + Supervisor *suture.Supervisor + Services []serviceFuncMap + Additional serviceFuncMap + Log log.Logger serviceToken map[string][]suture.ServiceToken context context.Context @@ -108,11 +110,9 @@ func NewService(options ...Option) (*Service, error) { globalCtx, cancelGlobal := context.WithCancel(context.Background()) s := &Service{ - Preliminary: make(serviceFuncMap), - ServicesRegistry: make(serviceFuncMap), - Delayed: make(serviceFuncMap), - Additional: make(serviceFuncMap), - Log: l, + Services: make([]serviceFuncMap, len(_waitFuncs)), + Additional: make(serviceFuncMap), + Log: l, serviceToken: make(map[string][]suture.ServiceToken), context: globalCtx, @@ -120,162 +120,200 @@ func NewService(options ...Option) (*Service, error) { cfg: opts.Config, } - // start nats first - it is used as service registry - s.Preliminary[opts.Config.Nats.Service.Name] = NewSutureServiceBuilder(func(ctx context.Context, cfg *ociscfg.Config) error { + // populate services + reg := func(priority int, name string, exec func(context.Context, *ociscfg.Config) error) { + if s.Services[priority] == nil { + s.Services[priority] = make(serviceFuncMap) + } + s.Services[priority][name] = NewSutureServiceBuilder(exec) + } + + // nats is in priority group 0. It needs to start before all other services + reg(0, opts.Config.Nats.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Nats.Context = ctx cfg.Nats.Commons = cfg.Commons return nats.Execute(cfg.Nats) }) - // populate services - reg := func(name string, exec func(context.Context, *ociscfg.Config) error) { - s.ServicesRegistry[name] = NewSutureServiceBuilder(exec) - } - reg(opts.Config.AppProvider.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.AppProvider.Context = ctx - cfg.AppProvider.Commons = cfg.Commons - return appProvider.Execute(cfg.AppProvider) - }) - reg(opts.Config.AppRegistry.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.AppRegistry.Context = ctx - cfg.AppRegistry.Commons = cfg.Commons - return appRegistry.Execute(cfg.AppRegistry) - }) - reg(opts.Config.AuthBasic.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.AuthBasic.Context = ctx - cfg.AuthBasic.Commons = cfg.Commons - return authbasic.Execute(cfg.AuthBasic) - }) - reg(opts.Config.AuthMachine.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.AuthMachine.Context = ctx - cfg.AuthMachine.Commons = cfg.Commons - return authmachine.Execute(cfg.AuthMachine) - }) - reg(opts.Config.AuthService.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.AuthService.Context = ctx - cfg.AuthService.Commons = cfg.Commons - return authservice.Execute(cfg.AuthService) - }) - reg(opts.Config.Clientlog.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.Clientlog.Context = ctx - cfg.Clientlog.Commons = cfg.Commons - return clientlog.Execute(cfg.Clientlog) - }) - reg(opts.Config.EventHistory.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.EventHistory.Context = ctx - cfg.EventHistory.Commons = cfg.Commons - return eventhistory.Execute(cfg.EventHistory) - }) - reg(opts.Config.Gateway.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + // gateway is in priority group 1. It needs to start before the reva services + reg(1, opts.Config.Gateway.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Gateway.Context = ctx cfg.Gateway.Commons = cfg.Commons return gateway.Execute(cfg.Gateway) }) - reg(opts.Config.Graph.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + + // priority group 2 is empty for now + + // most services are in priority group 3 + reg(3, opts.Config.AppProvider.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AppProvider.Context = ctx + cfg.AppProvider.Commons = cfg.Commons + return appProvider.Execute(cfg.AppProvider) + }) + reg(3, opts.Config.AppRegistry.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AppRegistry.Context = ctx + cfg.AppRegistry.Commons = cfg.Commons + return appRegistry.Execute(cfg.AppRegistry) + }) + reg(3, opts.Config.AuthBasic.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AuthBasic.Context = ctx + cfg.AuthBasic.Commons = cfg.Commons + return authbasic.Execute(cfg.AuthBasic) + }) + reg(3, opts.Config.AuthMachine.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AuthMachine.Context = ctx + cfg.AuthMachine.Commons = cfg.Commons + return authmachine.Execute(cfg.AuthMachine) + }) + reg(3, opts.Config.AuthService.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.AuthService.Context = ctx + cfg.AuthService.Commons = cfg.Commons + return authservice.Execute(cfg.AuthService) + }) + reg(3, opts.Config.Clientlog.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.Clientlog.Context = ctx + cfg.Clientlog.Commons = cfg.Commons + return clientlog.Execute(cfg.Clientlog) + }) + reg(3, opts.Config.EventHistory.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.EventHistory.Context = ctx + cfg.EventHistory.Commons = cfg.Commons + return eventhistory.Execute(cfg.EventHistory) + }) + reg(3, opts.Config.Graph.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Graph.Context = ctx cfg.Graph.Commons = cfg.Commons return graph.Execute(cfg.Graph) }) - reg(opts.Config.Groups.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Groups.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Groups.Context = ctx cfg.Groups.Commons = cfg.Commons return groups.Execute(cfg.Groups) }) - reg(opts.Config.IDM.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.IDM.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.IDM.Context = ctx cfg.IDM.Commons = cfg.Commons return idm.Execute(cfg.IDM) }) - reg(opts.Config.Invitations.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.Invitations.Context = ctx - cfg.Invitations.Commons = cfg.Commons - return invitations.Execute(cfg.Invitations) - }) - reg(opts.Config.Notifications.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Notifications.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Notifications.Context = ctx cfg.Notifications.Commons = cfg.Commons return notifications.Execute(cfg.Notifications) }) - reg(opts.Config.OCDav.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.OCDav.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.OCDav.Context = ctx cfg.OCDav.Commons = cfg.Commons return ocdav.Execute(cfg.OCDav) }) - reg(opts.Config.OCS.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.OCS.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.OCS.Context = ctx cfg.OCS.Commons = cfg.Commons return ocs.Execute(cfg.OCS) }) - reg(opts.Config.Postprocessing.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Postprocessing.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Postprocessing.Context = ctx cfg.Postprocessing.Commons = cfg.Commons return postprocessing.Execute(cfg.Postprocessing) }) - reg(opts.Config.Search.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Search.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Search.Context = ctx cfg.Search.Commons = cfg.Commons return search.Execute(cfg.Search) }) - reg(opts.Config.Settings.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Settings.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Settings.Context = ctx cfg.Settings.Commons = cfg.Commons return settings.Execute(cfg.Settings) }) - reg(opts.Config.StoragePublicLink.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.StoragePublicLink.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.StoragePublicLink.Context = ctx cfg.StoragePublicLink.Commons = cfg.Commons return storagepublic.Execute(cfg.StoragePublicLink) }) - reg(opts.Config.StorageShares.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.StorageShares.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.StorageShares.Context = ctx cfg.StorageShares.Commons = cfg.Commons return storageshares.Execute(cfg.StorageShares) }) - reg(opts.Config.StorageSystem.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.StorageSystem.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.StorageSystem.Context = ctx cfg.StorageSystem.Commons = cfg.Commons return storageSystem.Execute(cfg.StorageSystem) }) - reg(opts.Config.StorageUsers.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.StorageUsers.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.StorageUsers.Context = ctx cfg.StorageUsers.Commons = cfg.Commons return storageusers.Execute(cfg.StorageUsers) }) - reg(opts.Config.Store.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Store.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Store.Context = ctx cfg.Store.Commons = cfg.Commons return store.Execute(cfg.Store) }) - reg(opts.Config.Thumbnails.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Thumbnails.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Thumbnails.Context = ctx cfg.Thumbnails.Commons = cfg.Commons return thumbnails.Execute(cfg.Thumbnails) }) - reg(opts.Config.Userlog.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Userlog.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Userlog.Context = ctx cfg.Userlog.Commons = cfg.Commons return userlog.Execute(cfg.Userlog) }) - reg(opts.Config.Users.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Users.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Users.Context = ctx cfg.Users.Commons = cfg.Commons return users.Execute(cfg.Users) }) - reg(opts.Config.Web.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Web.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Web.Context = ctx cfg.Web.Commons = cfg.Commons return web.Execute(cfg.Web) }) - reg(opts.Config.WebDAV.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.WebDAV.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.WebDAV.Context = ctx cfg.WebDAV.Commons = cfg.Commons return webdav.Execute(cfg.WebDAV) }) - reg(opts.Config.Webfinger.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + reg(3, opts.Config.Webfinger.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { cfg.Webfinger.Context = ctx cfg.Webfinger.Commons = cfg.Commons return webfinger.Execute(cfg.Webfinger) }) + reg(3, opts.Config.IDP.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.IDP.Context = ctx + cfg.IDP.Commons = cfg.Commons + return idp.Execute(cfg.IDP) + }) + reg(3, opts.Config.Proxy.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.Proxy.Context = ctx + cfg.Proxy.Commons = cfg.Commons + return proxy.Execute(cfg.Proxy) + }) + reg(3, opts.Config.Sharing.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.Sharing.Context = ctx + cfg.Sharing.Commons = cfg.Commons + return sharing.Execute(cfg.Sharing) + }) + reg(3, opts.Config.SSE.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.SSE.Context = ctx + cfg.SSE.Commons = cfg.Commons + return sse.Execute(cfg.SSE) + }) + reg(3, opts.Config.OCM.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.OCM.Context = ctx + cfg.OCM.Commons = cfg.Commons + return ocm.Execute(cfg.OCM) + }) + + // out of some unknown reason ci gets angry when frontend service starts in priority group 3 + // this is not reproducible locally, it can start when nats and gateway are already running + // FIXME: find out why + reg(4, opts.Config.Frontend.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.Frontend.Context = ctx + cfg.Frontend.Commons = cfg.Commons + return frontend.Execute(cfg.Frontend) + }) // populate optional services areg := func(name string, exec func(context.Context, *ociscfg.Config) error) { @@ -296,40 +334,10 @@ func NewService(options ...Option) (*Service, error) { cfg.Policies.Commons = cfg.Commons return policies.Execute(cfg.Policies) }) - - // populate delayed services - dreg := func(name string, exec func(context.Context, *ociscfg.Config) error) { - s.Delayed[name] = NewSutureServiceBuilder(exec) - } - dreg(opts.Config.Frontend.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.Frontend.Context = ctx - cfg.Frontend.Commons = cfg.Commons - return frontend.Execute(cfg.Frontend) - }) - dreg(opts.Config.IDP.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.IDP.Context = ctx - cfg.IDP.Commons = cfg.Commons - return idp.Execute(cfg.IDP) - }) - dreg(opts.Config.Proxy.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.Proxy.Context = ctx - cfg.Proxy.Commons = cfg.Commons - return proxy.Execute(cfg.Proxy) - }) - dreg(opts.Config.Sharing.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.Sharing.Context = ctx - cfg.Sharing.Commons = cfg.Commons - return sharing.Execute(cfg.Sharing) - }) - dreg(opts.Config.SSE.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.SSE.Context = ctx - cfg.SSE.Commons = cfg.Commons - return sse.Execute(cfg.SSE) - }) - dreg(opts.Config.OCM.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { - cfg.OCM.Context = ctx - cfg.OCM.Commons = cfg.Commons - return ocm.Execute(cfg.OCM) + areg(opts.Config.Invitations.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error { + cfg.Invitations.Context = ctx + cfg.Invitations.Commons = cfg.Commons + return invitations.Execute(cfg.Invitations) }) return s, nil @@ -378,14 +386,14 @@ func Start(o ...Option) error { if err = rpc.Register(s); err != nil { if s != nil { - s.Log.Fatal().Err(err) + s.Log.Fatal().Err(err).Msg("could not register rpc service") } } rpc.HandleHTTP() l, err := net.Listen("tcp", net.JoinHostPort(s.cfg.Runtime.Host, s.cfg.Runtime.Port)) if err != nil { - s.Log.Fatal().Err(err) + s.Log.Fatal().Err(err).Msg("could not start listener") } defer func() { @@ -402,9 +410,6 @@ func Start(o ...Option) error { // prepare the set of services to run s.generateRunSet(s.cfg) - // schedule preliminary services first - scheduleServiceTokens(s, s.Preliminary) - // there are reasons not to do this, but we have race conditions ourselves. Until we resolve them, mind the following disclaimer: // Calling ServeBackground will CORRECTLY start the supervisor running in a new goroutine. It is risky to directly run // go supervisor.Serve() @@ -415,19 +420,18 @@ func Start(o ...Option) error { // trap will block on halt channel for interruptions. go trap(s, halt) - // grace period for preliminary services to get up - time.Sleep(_preliminaryDelay) - - // schedule services that we are sure don't have interdependencies. - scheduleServiceTokens(s, s.ServicesRegistry) + for i, service := range s.Services { + scheduleServiceTokens(s, service) + if _waitFuncs[i] != nil { + if err := _waitFuncs[i](s.cfg); err != nil { + s.Log.Fatal().Err(err).Msg("wait func failed") + } + } + } // schedule services that are optional scheduleServiceTokens(s, s.Additional) - // add services with delayed execution. - time.Sleep(_startDelay) - scheduleServiceTokens(s, s.Delayed) - return http.Serve(l, nil) } @@ -454,16 +458,10 @@ func (s *Service) generateRunSet(cfg *ociscfg.Config) { return } - for name := range s.Preliminary { - runset[name] = struct{}{} - } - - for name := range s.ServicesRegistry { - runset[name] = struct{}{} - } - - for name := range s.Delayed { - runset[name] = struct{}{} + for _, service := range s.Services { + for name := range service { + runset[name] = struct{}{} + } } // add additional services if explicitly added by config @@ -516,3 +514,39 @@ func trap(s *Service, halt chan os.Signal) { s.Log.Debug().Str("service", "runtime service").Msgf("terminating with signal: %v", s) os.Exit(0) } + +// pingNats will attempt to connect to nats, blocking until a connection is established +func pingNats(cfg *ociscfg.Config) error { + // We need to get a natsconfig from somewhere. We can use any one. + evcfg := cfg.Postprocessing.Postprocessing.Events + _, err := stream.NatsFromConfig("initial", true, stream.NatsConfig(evcfg)) + return err +} + +func pingGateway(_ *ociscfg.Config) error { + // init grpc connection + _, err := ogrpc.NewClient() + if err != nil { + return err + } + + b := backoff.NewExponentialBackOff() + o := func() error { + n := b.NextBackOff() + _, err := pool.GetGatewayServiceClient("com.owncloud.api.gateway") + if err != nil && n > time.Second { + logger.New().Error().Err(err).Msgf("can't connect to gateway service, retrying in %s", n) + } + return err + } + + err = backoff.Retry(o, b) + return err +} + +func wait(d time.Duration) func(cfg *ociscfg.Config) error { + return func(cfg *ociscfg.Config) error { + time.Sleep(d) + return nil + } +} diff --git a/services/audit/pkg/config/config.go b/services/audit/pkg/config/config.go index 0ee9e5493..f0ea50bb9 100644 --- a/services/audit/pkg/config/config.go +++ b/services/audit/pkg/config/config.go @@ -28,9 +28,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;AUDIT_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;AUDIT_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;AUDIT_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided AUDIT_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;AUDIT_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;AUDIT_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;AUDIT_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;AUDIT_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;AUDIT_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;AUDIT_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // Auditlog holds audit log information diff --git a/services/clientlog/pkg/config/config.go b/services/clientlog/pkg/config/config.go index 921dc2737..aa1d8b12b 100644 --- a/services/clientlog/pkg/config/config.go +++ b/services/clientlog/pkg/config/config.go @@ -34,9 +34,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;CLIENTLOG_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;CLIENTLOG_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;CLIENTLOG_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;CLIENTLOG_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;CLIENTLOG_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;CLIENTLOG_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;CLIENTLOG_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;CLIENTLOG_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;CLIENTLOG_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // TokenManager is the config for using the reva token manager diff --git a/services/clientlog/pkg/service/service.go b/services/clientlog/pkg/service/service.go index 22b61cc0c..f905c8984 100644 --- a/services/clientlog/pkg/service/service.go +++ b/services/clientlog/pkg/service/service.go @@ -8,13 +8,15 @@ import ( "reflect" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "go.opentelemetry.io/otel/trace" + "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/clientlog/pkg/config" - "go.opentelemetry.io/otel/trace" ) // ClientlogService is the service responsible for user activities @@ -116,22 +118,20 @@ func (cl *ClientlogService) processEvent(event events.Event) { } // II) instruct sse service to send the information - for _, id := range users { - if err := cl.sendSSE(id, evType, data); err != nil { - cl.log.Error().Err(err).Str("userID", id).Str("eventid", event.ID).Msg("failed to store event for user") - return - } + if err := cl.sendSSE(users, evType, data); err != nil { + cl.log.Error().Err(err).Interface("userIDs", users).Str("eventid", event.ID).Msg("failed to store event for user") + return } } -func (cl *ClientlogService) sendSSE(userid string, evType string, data interface{}) error { +func (cl *ClientlogService) sendSSE(userIDs []string, evType string, data interface{}) error { b, err := json.Marshal(data) if err != nil { return err } return events.Publish(context.Background(), cl.publisher, events.SendSSE{ - UserID: userid, + UserIDs: userIDs, Type: evType, Message: b, }) diff --git a/services/eventhistory/pkg/config/config.go b/services/eventhistory/pkg/config/config.go index 84ffa4506..15c6229d5 100644 --- a/services/eventhistory/pkg/config/config.go +++ b/services/eventhistory/pkg/config/config.go @@ -53,7 +53,7 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;EVENTHISTORY_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;EVENTHISTORY_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;EVENTHISTORY_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. Will be seen as empty if NOTIFICATIONS_EVENTS_TLS_INSECURE is provided."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;EVENTHISTORY_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;EVENTHISTORY_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;EVENTHISTORY_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;EVENTHISTORY_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;EVENTHISTORY_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;EVENTHISTORY_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go index 2c2aea74c..f620c6a5f 100644 --- a/services/frontend/pkg/config/config.go +++ b/services/frontend/pkg/config/config.go @@ -171,9 +171,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;FRONTEND_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;FRONTEND_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"FRONTEND_EVENTS_TLS_ROOT_CA_CERTIFICATE;OCS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;FRONTEND_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;FRONTEND_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;FRONTEND_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;FRONTEND_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;FRONTEND_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;FRONTEND_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // ServiceAccount is the configuration for the used service account diff --git a/services/graph/mocks/http_client.go b/services/graph/mocks/http_client.go index 6baaf3319..b31a16b26 100644 --- a/services/graph/mocks/http_client.go +++ b/services/graph/mocks/http_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/mocks/permissions.go b/services/graph/mocks/permissions.go index 73bf9421d..a0645ce95 100644 --- a/services/graph/mocks/permissions.go +++ b/services/graph/mocks/permissions.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/mocks/publisher.go b/services/graph/mocks/publisher.go index 189ebe218..7449caf0f 100644 --- a/services/graph/mocks/publisher.go +++ b/services/graph/mocks/publisher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/mocks/role_service.go b/services/graph/mocks/role_service.go index 70fadd24e..4538a9430 100644 --- a/services/graph/mocks/role_service.go +++ b/services/graph/mocks/role_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go index 3f2212270..7bfbfd3cd 100644 --- a/services/graph/pkg/config/config.go +++ b/services/graph/pkg/config/config.go @@ -120,9 +120,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;GRAPH_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;GRAPH_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;GRAPH_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided GRAPH_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;GRAPH_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;GRAPH_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;GRAPH_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;GRAPH_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;GRAPH_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;GRAPH_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // CORS defines the available cors configuration. diff --git a/services/graph/pkg/identity/mocks/backend.go b/services/graph/pkg/identity/mocks/backend.go index 885ee880a..4a6153290 100644 --- a/services/graph/pkg/identity/mocks/backend.go +++ b/services/graph/pkg/identity/mocks/backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/pkg/identity/mocks/education_backend.go b/services/graph/pkg/identity/mocks/education_backend.go index 44e831c58..8799d04ad 100644 --- a/services/graph/pkg/identity/mocks/education_backend.go +++ b/services/graph/pkg/identity/mocks/education_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/pkg/identity/mocks/ldapclient.go b/services/graph/pkg/identity/mocks/ldapclient.go index f9f1e15b6..c4dba72e1 100644 --- a/services/graph/pkg/identity/mocks/ldapclient.go +++ b/services/graph/pkg/identity/mocks/ldapclient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/graph/pkg/service/v0/links.go b/services/graph/pkg/service/v0/links.go index f9303fa5c..684dd8e67 100644 --- a/services/graph/pkg/service/v0/links.go +++ b/services/graph/pkg/service/v0/links.go @@ -202,7 +202,7 @@ func (g Graph) libreGraphPermissionFromCS3PublicShare(createdLink *link.PublicSh } func parseAndFillUpTime(t *time.Time) *types.Timestamp { - if t == nil { + if t == nil || t.IsZero() { return nil } diff --git a/services/invitations/pkg/backends/keycloak/mocks/client.go b/services/invitations/pkg/backends/keycloak/mocks/client.go index be451669b..c6bdfbc7c 100644 --- a/services/invitations/pkg/backends/keycloak/mocks/client.go +++ b/services/invitations/pkg/backends/keycloak/mocks/client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/nats/pkg/config/config.go b/services/nats/pkg/config/config.go index 47427b22f..7d3d83aa6 100644 --- a/services/nats/pkg/config/config.go +++ b/services/nats/pkg/config/config.go @@ -28,7 +28,7 @@ type Nats struct { TLSCert string `yaml:"tls_cert" env:"NATS_TLS_CERT" desc:"Path/File name of the TLS server certificate (in PEM format) for the NATS listener. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/nats."` TLSKey string `yaml:"tls_key" env:"NATS_TLS_KEY" desc:"Path/File name for the TLS certificate key (in PEM format) for the NATS listener. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/nats."` TLSSkipVerifyClientCert bool `yaml:"tls_skip_verify_client_cert" env:"OCIS_INSECURE;NATS_TLS_SKIP_VERIFY_CLIENT_CERT" desc:"Whether the NATS server should skip the client certificate verification during the TLS handshake."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NATS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NATS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // Tracing is the tracing config diff --git a/services/notifications/pkg/config/config.go b/services/notifications/pkg/config/config.go index 8ea2ec174..75cea110c 100644 --- a/services/notifications/pkg/config/config.go +++ b/services/notifications/pkg/config/config.go @@ -55,9 +55,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;NOTIFICATIONS_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;NOTIFICATIONS_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;NOTIFICATIONS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NOTIFICATIONS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;NOTIFICATIONS_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;NOTIFICATIONS_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NOTIFICATIONS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;NOTIFICATIONS_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;NOTIFICATIONS_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // ServiceAccount is the configuration for the used service account diff --git a/services/notifications/pkg/email/l10n/locale/de/LC_MESSAGES/notifications.po b/services/notifications/pkg/email/l10n/locale/de/LC_MESSAGES/notifications.po index 6d609dd2c..f98e58b8b 100644 --- a/services/notifications/pkg/email/l10n/locale/de/LC_MESSAGES/notifications.po +++ b/services/notifications/pkg/email/l10n/locale/de/LC_MESSAGES/notifications.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-04-19 11:11+0000\n" "Last-Translator: Michael Barz , 2023\n" "Language-Team: German (https://app.transifex.com/owncloud-org/teams/6149/de/)\n" diff --git a/services/notifications/pkg/email/l10n/locale/de_DE/LC_MESSAGES/notifications.po b/services/notifications/pkg/email/l10n/locale/de_DE/LC_MESSAGES/notifications.po index baf751ee2..23f39d6e1 100644 --- a/services/notifications/pkg/email/l10n/locale/de_DE/LC_MESSAGES/notifications.po +++ b/services/notifications/pkg/email/l10n/locale/de_DE/LC_MESSAGES/notifications.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-04-19 11:11+0000\n" "Last-Translator: Michael Barz , 2023\n" "Language-Team: German (Germany) (https://app.transifex.com/owncloud-org/teams/6149/de_DE/)\n" diff --git a/services/notifications/pkg/email/l10n/locale/tr/LC_MESSAGES/notifications.po b/services/notifications/pkg/email/l10n/locale/tr/LC_MESSAGES/notifications.po index 4fee69de5..537615b17 100644 --- a/services/notifications/pkg/email/l10n/locale/tr/LC_MESSAGES/notifications.po +++ b/services/notifications/pkg/email/l10n/locale/tr/LC_MESSAGES/notifications.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-04-19 11:11+0000\n" "Last-Translator: Begüm Topyıldız , 2023\n" "Language-Team: Turkish (https://app.transifex.com/owncloud-org/teams/6149/tr/)\n" diff --git a/services/notifications/pkg/email/l10n/locale/zh-Hans/LC_MESSAGES/notifications.po b/services/notifications/pkg/email/l10n/locale/zh-Hans/LC_MESSAGES/notifications.po index 30b752ee3..eee02c1c5 100644 --- a/services/notifications/pkg/email/l10n/locale/zh-Hans/LC_MESSAGES/notifications.po +++ b/services/notifications/pkg/email/l10n/locale/zh-Hans/LC_MESSAGES/notifications.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-04-19 11:11+0000\n" "Last-Translator: Shouyuan, 2023\n" "Language-Team: Chinese Simplified (https://app.transifex.com/owncloud-org/teams/6149/zh-Hans/)\n" diff --git a/services/notifications/pkg/email/l10n/locale/zh_CN/LC_MESSAGES/notifications.po b/services/notifications/pkg/email/l10n/locale/zh_CN/LC_MESSAGES/notifications.po index 4bcf5c6c0..9b19507d2 100644 --- a/services/notifications/pkg/email/l10n/locale/zh_CN/LC_MESSAGES/notifications.po +++ b/services/notifications/pkg/email/l10n/locale/zh_CN/LC_MESSAGES/notifications.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-04-19 11:11+0000\n" "Last-Translator: Shouyuan, 2023\n" "Language-Team: Chinese (China) (https://app.transifex.com/owncloud-org/teams/6149/zh_CN/)\n" diff --git a/services/proxy/pkg/user/backend/mocks/user_backend.go b/services/proxy/pkg/user/backend/mocks/user_backend.go index c625438af..2d57b3401 100644 --- a/services/proxy/pkg/user/backend/mocks/user_backend.go +++ b/services/proxy/pkg/user/backend/mocks/user_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/proxy/pkg/userroles/mocks/user_role_assigner.go b/services/proxy/pkg/userroles/mocks/user_role_assigner.go index 3a601b6aa..babeb5491 100644 --- a/services/proxy/pkg/userroles/mocks/user_role_assigner.go +++ b/services/proxy/pkg/userroles/mocks/user_role_assigner.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/search/pkg/content/mocks/extractor.go b/services/search/pkg/content/mocks/extractor.go index 80a795af3..273d96b01 100644 --- a/services/search/pkg/content/mocks/extractor.go +++ b/services/search/pkg/content/mocks/extractor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/search/pkg/content/mocks/retriever.go b/services/search/pkg/content/mocks/retriever.go index 76271c220..c755da70e 100644 --- a/services/search/pkg/content/mocks/retriever.go +++ b/services/search/pkg/content/mocks/retriever.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/search/pkg/engine/bleve.go b/services/search/pkg/engine/bleve.go index a0b3f26f3..54375d992 100644 --- a/services/search/pkg/engine/bleve.go +++ b/services/search/pkg/engine/bleve.go @@ -22,6 +22,7 @@ import ( storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" @@ -123,6 +124,9 @@ func BuildBleveMapping() (mapping.IndexMapping, error) { func (b *Bleve) Search(ctx context.Context, sir *searchService.SearchIndexRequest) (*searchService.SearchIndexResponse, error) { createdQuery, err := b.queryCreator.Create(sir.Query) if err != nil { + if searchQuery.IsValidationError(err) { + return nil, errtypes.BadRequest(err.Error()) + } return nil, err } diff --git a/services/search/pkg/engine/mocks/engine.go b/services/search/pkg/engine/mocks/engine.go index aca2f730f..d1c356d0c 100644 --- a/services/search/pkg/engine/mocks/engine.go +++ b/services/search/pkg/engine/mocks/engine.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/search/pkg/query/kql/error.go b/services/search/pkg/query/error.go similarity index 82% rename from services/search/pkg/query/kql/error.go rename to services/search/pkg/query/error.go index 5f171d77f..89f504c8a 100644 --- a/services/search/pkg/query/kql/error.go +++ b/services/search/pkg/query/error.go @@ -1,4 +1,4 @@ -package kql +package query import ( "fmt" @@ -37,3 +37,11 @@ type UnsupportedTimeRangeError struct { func (e UnsupportedTimeRangeError) Error() string { return fmt.Sprintf("unable to convert '%v' to a time range", e.Value) } + +func IsValidationError(err error) bool { + switch err.(type) { + case *StartsWithBinaryOperatorError, *NamedGroupInvalidNodesError, *UnsupportedTimeRangeError: + return true + } + return false +} diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index 06fb800ce..c6ca30541 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -5,6 +5,7 @@ import ( "time" "github.com/jinzhu/now" + "github.com/owncloud/ocis/v2/services/search/pkg/query" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -84,7 +85,7 @@ func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { value, err := toString(in) if err != nil { - return &from, &to, UnsupportedTimeRangeError{} + return &from, &to, &query.UnsupportedTimeRangeError{} } c := &now.Config{ @@ -131,7 +132,7 @@ func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { } if from.IsZero() || to.IsZero() { - return nil, nil, UnsupportedTimeRangeError{} + return nil, nil, &query.UnsupportedTimeRangeError{} } return &from, &to, nil diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index ba2780db8..adf4bd2d4 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/jinzhu/now" + "github.com/owncloud/ocis/v2/services/search/pkg/query" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -34,13 +35,13 @@ func TestParse_Spec(t *testing.T) { }, { name: `AND`, - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { name: `AND cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, @@ -80,13 +81,13 @@ func TestParse_Spec(t *testing.T) { }, { name: `OR`, - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { name: `OR cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, @@ -862,37 +863,37 @@ func TestParse_Errors(t *testing.T) { tests := []testCase{ { query: "animal:(mammal:cat mammal:dog reptile:turtle)", - error: kql.NamedGroupInvalidNodesError{ + error: query.NamedGroupInvalidNodesError{ Node: &ast.StringNode{Key: "mammal", Value: "cat"}, }, }, { query: "animal:(cat mammal:dog turtle)", - error: kql.NamedGroupInvalidNodesError{ + error: query.NamedGroupInvalidNodesError{ Node: &ast.StringNode{Key: "mammal", Value: "dog"}, }, }, { query: "animal:(AND cat)", - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { query: "animal:(OR cat)", - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { query: "(AND cat)", - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { query: "(OR cat)", - error: kql.StartsWithBinaryOperatorError{ + error: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, diff --git a/services/search/pkg/query/kql/kql_test.go b/services/search/pkg/query/kql/kql_test.go index de050a474..c130f7558 100644 --- a/services/search/pkg/query/kql/kql_test.go +++ b/services/search/pkg/query/kql/kql_test.go @@ -3,6 +3,7 @@ package kql_test import ( "testing" + "github.com/owncloud/ocis/v2/services/search/pkg/query" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -22,7 +23,7 @@ func TestNewAST(t *testing.T) { { name: "error", givenQuery: kql.BoolAND, - expectedError: kql.StartsWithBinaryOperatorError{ + expectedError: query.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, diff --git a/services/search/pkg/query/kql/validate.go b/services/search/pkg/query/kql/validate.go index 9e9a2428b..96c6761ab 100644 --- a/services/search/pkg/query/kql/validate.go +++ b/services/search/pkg/query/kql/validate.go @@ -1,6 +1,7 @@ package kql import ( + "github.com/owncloud/ocis/v2/services/search/pkg/query" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -9,10 +10,9 @@ func validateAst(a *ast.Ast) error { case *ast.OperatorNode: switch node.Value { case BoolAND, BoolOR: - return StartsWithBinaryOperatorError{Node: node} + return &query.StartsWithBinaryOperatorError{Node: node} } } - return nil } @@ -21,14 +21,14 @@ func validateGroupNode(n *ast.GroupNode) error { case *ast.OperatorNode: switch node.Value { case BoolAND, BoolOR: - return StartsWithBinaryOperatorError{Node: node} + return &query.StartsWithBinaryOperatorError{Node: node} } } if n.Key != "" { for _, node := range n.Nodes { if ast.NodeKey(node) != "" { - return NamedGroupInvalidNodesError{Node: node} + return &query.NamedGroupInvalidNodesError{Node: node} } } } diff --git a/services/search/pkg/search/mocks/searcher.go b/services/search/pkg/search/mocks/searcher.go index 8900f1dc2..cb0b0b529 100644 --- a/services/search/pkg/search/mocks/searcher.go +++ b/services/search/pkg/search/mocks/searcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/settings/pkg/settings/mocks/manager.go b/services/settings/pkg/settings/mocks/manager.go index 687d16888..b4d6b43c6 100644 --- a/services/settings/pkg/settings/mocks/manager.go +++ b/services/settings/pkg/settings/mocks/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.40.2. DO NOT EDIT. package mocks diff --git a/services/sharing/pkg/config/config.go b/services/sharing/pkg/config/config.go index 7d51d0b83..f7fc47b6d 100644 --- a/services/sharing/pkg/config/config.go +++ b/services/sharing/pkg/config/config.go @@ -152,7 +152,7 @@ type Events struct { ClusterID string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;SHARING_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;SHARING_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCaCertPath string `yaml:"tls_root_ca_cert_path" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;SHARING_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;SHARING_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` AuthUsername string `yaml:"auth_username" env:"OCIS_EVENTS_AUTH_USERNAME;SHARING_EVENTS_AUTH_USERNAME" desc:"Username for the events broker."` AuthPassword string `yaml:"auth_password" env:"OCIS_EVENTS_AUTH_PASSWORD;SHARING_EVENTS_AUTH_PASSWORD" desc:"Password for the events broker."` } diff --git a/services/sse/pkg/service/service.go b/services/sse/pkg/service/service.go index 23979d866..aac0cdd42 100644 --- a/services/sse/pkg/service/service.go +++ b/services/sse/pkg/service/service.go @@ -3,11 +3,12 @@ package service import ( "net/http" - revactx "github.com/cs3org/reva/v2/pkg/ctx" - "github.com/cs3org/reva/v2/pkg/events" "github.com/go-chi/chi/v5" "github.com/r3labs/sse/v2" + revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/events" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/sse/pkg/config" ) @@ -51,10 +52,12 @@ func (s SSE) ListenForEvents() { default: s.l.Error().Interface("event", ev).Msg("unhandled event") case events.SendSSE: - s.sse.Publish(ev.UserID, &sse.Event{ - Event: []byte(ev.Type), - Data: ev.Message, - }) + for _, uid := range ev.UserIDs { + s.sse.Publish(uid, &sse.Event{ + Event: []byte(ev.Type), + Data: ev.Message, + }) + } } } } diff --git a/services/storage-users/pkg/config/config.go b/services/storage-users/pkg/config/config.go index 8fd16c74b..43cdaa61d 100644 --- a/services/storage-users/pkg/config/config.go +++ b/services/storage-users/pkg/config/config.go @@ -175,10 +175,10 @@ type Events struct { ClusterID string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;STORAGE_USERS_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;STORAGE_USERS_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCaCertPath string `yaml:"tls_root_ca_cert_path" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;STORAGE_USERS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided STORAGE_USERS_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;STORAGE_USERS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;STORAGE_USERS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` NumConsumers int `yaml:"num_consumers" env:"STORAGE_USERS_EVENTS_NUM_CONSUMERS" desc:"The amount of concurrent event consumers to start. Event consumers are used for post-processing files. Multiple consumers increase parallelisation, but will also increase CPU and memory demands. The setting has no effect when the OCIS_ASYNC_UPLOADS is set to false. The default and minimum value is 1."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;STORAGE_USERS_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;STORAGE_USERS_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;STORAGE_USERS_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;STORAGE_USERS_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // FilemetadataCache holds cache config diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go index 9afde9a3c..f6b31563e 100644 --- a/services/userlog/pkg/config/config.go +++ b/services/userlog/pkg/config/config.go @@ -55,9 +55,9 @@ type Events struct { Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;USERLOG_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."` TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;USERLOG_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates."` TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;USERLOG_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."` - EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;USERLOG_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;USERLOG_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` - AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;USERLOG_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services.."` + EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;USERLOG_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;USERLOG_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` + AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;USERLOG_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services."` } // CORS defines the available cors configuration. diff --git a/services/userlog/pkg/service/l10n/locale/bg_BG/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/bg_BG/LC_MESSAGES/userlog.po index 167cc071a..452d8bfa1 100644 --- a/services/userlog/pkg/service/l10n/locale/bg_BG/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/bg_BG/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Martin Zhekov , 2023\n" "Language-Team: Bulgarian (Bulgaria) (https://app.transifex.com/owncloud-org/teams/6149/bg_BG/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po index 745f57977..d572b7c67 100644 --- a/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/de_DE/LC_MESSAGES/userlog.po @@ -5,17 +5,17 @@ # # Translators: # Michael Barz , 2023 -# Edith Parzefall, 2023 # Jürgen Weigert , 2023 +# Edith Parzefall, 2024 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-28 00:35+0000\n" +"POT-Creation-Date: 2024-02-15 01:14+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" -"Last-Translator: Jürgen Weigert , 2023\n" +"Last-Translator: Edith Parzefall, 2024\n" "Language-Team: German (Germany) (https://app.transifex.com/owncloud-org/teams/6149/de_DE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -68,7 +68,7 @@ msgstr "Ressource geteilt" #: pkg/service/templates.go:49 msgid "Resource unshared" -msgstr "Resource nicht mehr geteilt" +msgstr "Ressource nicht mehr geteilt" #: pkg/service/templates.go:54 msgid "Share expired" diff --git a/services/userlog/pkg/service/l10n/locale/en_GB/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/en_GB/LC_MESSAGES/userlog.po index 65bcfa993..1dfe325fd 100644 --- a/services/userlog/pkg/service/l10n/locale/en_GB/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/en_GB/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Andi Chandler , 2023\n" "Language-Team: English (United Kingdom) (https://app.transifex.com/owncloud-org/teams/6149/en_GB/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/es/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/es/LC_MESSAGES/userlog.po index 3d8574bee..8de6830c4 100644 --- a/services/userlog/pkg/service/l10n/locale/es/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/es/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-24 00:44+0000\n" +"POT-Creation-Date: 2024-02-13 06:53+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Juan Carlos Garrote, 2023\n" "Language-Team: Spanish (https://app.transifex.com/owncloud-org/teams/6149/es/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/ko/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/ko/LC_MESSAGES/userlog.po index 65147657b..8402e0077 100644 --- a/services/userlog/pkg/service/l10n/locale/ko/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/ko/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Hugo Blackwood, 2023\n" "Language-Team: Korean (https://app.transifex.com/owncloud-org/teams/6149/ko/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/pt_BR/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/pt_BR/LC_MESSAGES/userlog.po index 1ad4ac3c0..f5a140dd4 100644 --- a/services/userlog/pkg/service/l10n/locale/pt_BR/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/pt_BR/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Flávio Veras , 2023\n" "Language-Team: Portuguese (Brazil) (https://app.transifex.com/owncloud-org/teams/6149/pt_BR/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/ru/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/ru/LC_MESSAGES/userlog.po index 7438cabed..74981a5c8 100644 --- a/services/userlog/pkg/service/l10n/locale/ru/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/ru/LC_MESSAGES/userlog.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-18 00:47+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Roman Perekhod, 2023\n" "Language-Team: Russian (https://app.transifex.com/owncloud-org/teams/6149/ru/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/sq/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/sq/LC_MESSAGES/userlog.po index 5347f0216..dc4cbe4b1 100644 --- a/services/userlog/pkg/service/l10n/locale/sq/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/sq/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Besnik Bleta , 2023\n" "Language-Team: Albanian (https://app.transifex.com/owncloud-org/teams/6149/sq/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/tr/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/tr/LC_MESSAGES/userlog.po index cbfdbc0f6..c2298216d 100644 --- a/services/userlog/pkg/service/l10n/locale/tr/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/tr/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-19 00:48+0000\n" +"POT-Creation-Date: 2024-02-08 01:09+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Begüm Topyıldız , 2023\n" "Language-Team: Turkish (https://app.transifex.com/owncloud-org/teams/6149/tr/)\n" diff --git a/services/userlog/pkg/service/l10n/locale/zh_CN/LC_MESSAGES/userlog.po b/services/userlog/pkg/service/l10n/locale/zh_CN/LC_MESSAGES/userlog.po index 3d9b4fd62..fc79791ed 100644 --- a/services/userlog/pkg/service/l10n/locale/zh_CN/LC_MESSAGES/userlog.po +++ b/services/userlog/pkg/service/l10n/locale/zh_CN/LC_MESSAGES/userlog.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2024-01-14 00:06+0000\n" +"POT-Creation-Date: 2024-02-03 00:35+0000\n" "PO-Revision-Date: 2023-03-15 08:28+0000\n" "Last-Translator: Shouyuan, 2023\n" "Language-Team: Chinese (China) (https://app.transifex.com/owncloud-org/teams/6149/zh_CN/)\n" diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 684b9c8f3..696055de2 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -10,10 +10,14 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + "github.com/go-chi/chi/v5" + micrometadata "go-micro.dev/v4/metadata" + "go-micro.dev/v4/store" + "go.opentelemetry.io/otel/trace" + "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/go-chi/chi/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" "github.com/owncloud/ocis/v2/ocis-pkg/roles" @@ -22,9 +26,6 @@ import ( settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" "github.com/owncloud/ocis/v2/services/userlog/pkg/config" - micrometadata "go-micro.dev/v4/metadata" - "go-micro.dev/v4/store" - "go.opentelemetry.io/otel/trace" ) // UserlogService is the service responsible for user activities @@ -189,16 +190,18 @@ func (ul *UserlogService) processEvent(event events.Event) { // III) store the eventID for each user for _, id := range users { - if !ul.cfg.DisableSSE { - if err := ul.sendSSE(ctx, id, event, gwc); err != nil { - ul.log.Error().Err(err).Str("userid", id).Str("eventid", event.ID).Msg("cannot create sse event") - } - } - if err := ul.addEventToUser(ctx, id, event); err != nil { + if err := ul.addEventToUser(id, event); err != nil { ul.log.Error().Err(err).Str("userID", id).Str("eventid", event.ID).Msg("failed to store event for user") return } } + + // IV) send sses + if !ul.cfg.DisableSSE { + if err := ul.sendSSE(ctx, users, event, gwc); err != nil { + ul.log.Error().Err(err).Interface("userid", users).Str("eventid", event.ID).Msg("cannot create sse event") + } + } } // GetEvents allows retrieving events from the eventhistory by userid @@ -229,12 +232,12 @@ func (ul *UserlogService) GetEvents(ctx context.Context, userid string) ([]*ehms // remove expired events from list asynchronously go func() { - if err := ul.removeExpiredEvents(userid, eventIDs, resp.Events); err != nil { + if err := ul.removeExpiredEvents(userid, eventIDs, resp.GetEvents()); err != nil { ul.log.Error().Err(err).Str("userid", userid).Msg("could not remove expired events from user") } }() - return resp.Events, nil + return resp.GetEvents(), nil } // DeleteEvents will delete the specified events @@ -247,7 +250,7 @@ func (ul *UserlogService) DeleteEvents(userid string, evids []string) error { return ul.alterUserEventList(userid, func(ids []string) []string { var newids []string for _, id := range ids { - if _, delete := toDelete[id]; delete { + if _, del := toDelete[id]; del { continue } @@ -330,28 +333,52 @@ func (ul *UserlogService) DeleteGlobalEvents(ctx context.Context, evnames []stri }) } -func (ul *UserlogService) addEventToUser(ctx context.Context, userid string, event events.Event) error { +func (ul *UserlogService) addEventToUser(userid string, event events.Event) error { return ul.alterUserEventList(userid, func(ids []string) []string { return append(ids, event.ID) }) } -func (ul *UserlogService) sendSSE(ctx context.Context, userid string, event events.Event, gwc gateway.GatewayAPIClient) error { - ev, err := NewConverter(ctx, ul.getUserLocale(userid), gwc, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.cfg.DefaultLanguage).ConvertEvent(event.ID, event.Event) - if err != nil { - return err +func (ul *UserlogService) sendSSE(ctx context.Context, userIDs []string, event events.Event, gwc gateway.GatewayAPIClient) error { + m := make(map[string]events.SendSSE) + + for _, userid := range userIDs { + loc := ul.getUserLocale(userid) + if ev, ok := m[loc]; ok { + ev.UserIDs = append(m[loc].UserIDs, userid) + m[loc] = ev + continue + } + + ev, err := NewConverter(ctx, loc, gwc, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.cfg.DefaultLanguage).ConvertEvent(event.ID, event.Event) + if err != nil { + if utils.IsErrNotFound(err) || utils.IsErrPermissionDenied(err) { + // the resource was not found, we assume it is deleted + continue + } + return err + } + + b, err := json.Marshal(ev) + if err != nil { + return err + } + + m[loc] = events.SendSSE{ + UserIDs: []string{userid}, + Type: "userlog-notification", + Message: b, + } + } - b, err := json.Marshal(ev) - if err != nil { - return err + for _, ev := range m { + if err := events.Publish(ctx, ul.publisher, ev); err != nil { + return err + } } - return events.Publish(context.Background(), ul.publisher, events.SendSSE{ - UserID: userid, - Type: "userlog-notification", - Message: b, - }) + return nil } func (ul *UserlogService) removeExpiredEvents(userid string, all []string, received []*ehmsg.Event) error { diff --git a/services/web/Makefile b/services/web/Makefile index d25b19aec..b5da39a7b 100644 --- a/services/web/Makefile +++ b/services/web/Makefile @@ -1,6 +1,6 @@ SHELL := bash NAME := web -WEB_ASSETS_VERSION = v8.0.0-rc.3 +WEB_ASSETS_VERSION = v8.0.0-rc.4 include ../../.make/recursion.mk diff --git a/services/webdav/pkg/service/v0/search.go b/services/webdav/pkg/service/v0/search.go index 848b77f22..94c2efbb5 100644 --- a/services/webdav/pkg/service/v0/search.go +++ b/services/webdav/pkg/service/v0/search.go @@ -78,7 +78,7 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) { e := merrors.Parse(err.Error()) switch e.Code { case http.StatusBadRequest: - renderError(w, r, errBadRequest(err.Error())) + renderError(w, r, errBadRequest(e.Detail)) default: renderError(w, r, errInternalError(err.Error())) } diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php index 8ff0c9c25..b4f5863b4 100644 --- a/tests/TestHelpers/GraphHelper.php +++ b/tests/TestHelpers/GraphHelper.php @@ -1756,7 +1756,7 @@ class GraphHelper { * @return ResponseInterface * @throws GuzzleException */ - public static function updateLinkShare( + public static function updateShare( string $baseUrl, string $xRequestId, string $user, diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 0420c0ff5..b1a025e3c 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -231,16 +231,17 @@ default: - "%paths.base%/../features/apiSearch1" context: *common_ldap_suite_context contexts: + - FeatureContext: *common_feature_context_params - SettingsContext: - GraphContext: - SpacesContext: - PublicWebDavContext: - - FeatureContext: *common_feature_context_params - SearchContext: - OCSContext: - SpacesTUSContext: - TagContext: - TUSContext: + - WebDavPropertiesContext: apiSearch2: paths: @@ -316,6 +317,7 @@ default: - SharingNgContext: - PublicWebDavContext: - OcisConfigContext: + - SettingsContext: extensions: rdx\behatvars\BehatVariablesExtension: ~ diff --git a/tests/acceptance/expected-failures-API-on-OCIS-storage.md b/tests/acceptance/expected-failures-API-on-OCIS-storage.md index f53753854..9f10eec88 100644 --- a/tests/acceptance/expected-failures-API-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-API-on-OCIS-storage.md @@ -115,11 +115,6 @@ cannot share a folder with create permission - [coreApiSharePublicLink2/copyFromPublicLink.feature:193](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L193) - [coreApiSharePublicLink2/copyFromPublicLink.feature:194](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L194) -#### [Upload-only shares must not overwrite but create a separate file](https://github.com/owncloud/ocis/issues/1267) - -- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:13](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L13) -- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:121](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L121) - #### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197) - [coreApiWebdavProperties/getQuota.feature:55](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L55) diff --git a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md index 6f729a599..b8789d196 100644 --- a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md @@ -249,19 +249,19 @@ The expected failures in this file are from features in the owncloud/ocis repo. ### [blocksDownload link type is not implemented yet (sharing-ng)](https://github.com/owncloud/ocis/issues/7879) -- [apiSharingNg/linkShare.feature:79](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L79) -- [apiSharingNg/linkShare.feature:149](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L149) -- [apiSharingNg/linkShare.feature:228](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L228) -- [apiSharingNg/linkShare.feature:305](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L305) -- [apiSharingNg/linkShare.feature:377](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L377) -- [apiSharingNg/linkShare.feature:451](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L451) -- [apiSharingNg/linkShare.feature:453](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L453) -- [apiSharingNg/linkShare.feature:455](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L455) -- [apiSharingNg/linkShare.feature:456](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L456) -- [apiSharingNg/deletePermissions.feature:130](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L130) -- [apiSharingNg/deletePermissions.feature:147](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L147) -- [apiSharingNg/deletePermissions.feature:168](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L168) -- [apiSharingNg/deletePermissions.feature:187](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L187) +- [apiSharingNg/linkShare.feature:78](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L78) +- [apiSharingNg/linkShare.feature:147](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L147) +- [apiSharingNg/linkShare.feature:225](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L225) +- [apiSharingNg/linkShare.feature:301](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L301) +- [apiSharingNg/linkShare.feature:372](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L372) +- [apiSharingNg/linkShare.feature:444](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L444) +- [apiSharingNg/linkShare.feature:446](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L446) +- [apiSharingNg/linkShare.feature:448](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L448) +- [apiSharingNg/linkShare.feature:449](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/linkShare.feature#L449) +- [apiSharingNg/deletePermissions.feature:125](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L125) +- [apiSharingNg/deletePermissions.feature:141](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L141) +- [apiSharingNg/deletePermissions.feature:161](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L161) +- [apiSharingNg/deletePermissions.feature:179](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSharingNg/deletePermissions.feature#L179) ### [sharee (editor role) MOVE a file by file-id into same shared folder returns 403](https://github.com/owncloud/ocis/issues/7617) diff --git a/tests/acceptance/features/apiSearch1/apiSpaceSearch.feature b/tests/acceptance/features/apiSearch1/search.feature similarity index 78% rename from tests/acceptance/features/apiSearch1/apiSpaceSearch.feature rename to tests/acceptance/features/apiSearch1/search.feature index 715ad2c84..92ffe3fbe 100644 --- a/tests/acceptance/features/apiSearch1/apiSpaceSearch.feature +++ b/tests/acceptance/features/apiSearch1/search.feature @@ -3,9 +3,6 @@ Feature: Search I want to search for resources in the space So that I can get them quickly - Note - this feature is run in CI with ACCOUNTS_HASH_DIFFICULTY set to the default for production - See https://github.com/owncloud/ocis/issues/1542 and https://github.com/owncloud/ocis/pull/839 - Background: Given these users have been created with default attributes and without skeleton files: | username | @@ -13,29 +10,12 @@ Feature: Search | Brian | And using spaces DAV path And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API - And user "Alice" has created a space "find data" with the default quota using the Graph API - And user "Alice" has created a folder "folderMain/SubFolder1/subFOLDER2" in space "find data" - And user "Alice" has uploaded a file inside space "find data" with content "some content" to "folderMain/SubFolder1/subFOLDER2/insideTheFolder.txt" + And user "Alice" has created a space "project101" with the default quota using the Graph API + And user "Alice" has created a folder "folderMain/SubFolder1/subFOLDER2" in space "project101" + And user "Alice" has uploaded a file inside space "project101" with content "some content" to "folderMain/SubFolder1/subFOLDER2/insideTheFolder.txt" - Scenario Outline: user can find data from the project space - Given using DAV path - When user "Alice" searches for "*fol*" using the WebDAV API - Then the HTTP status code should be "207" - And the search result should contain "4" entries - And the search result of user "Alice" should contain these entries: - | /folderMain | - | /folderMain/SubFolder1 | - | /folderMain/SubFolder1/subFOLDER2 | - | /folderMain/SubFolder1/subFOLDER2/insideTheFolder.txt | - Examples: - | dav-path-version | - | old | - | new | - | spaces | - - - Scenario Outline: user can only find data that they searched for from the project space + Scenario Outline: user can search items from the project space Given using DAV path When user "Alice" searches for "*SUB*" using the WebDAV API Then the HTTP status code should be "207" @@ -53,9 +33,9 @@ Feature: Search | spaces | - Scenario Outline: user can find data from the shares + Scenario Outline: user can search items from the shares Given using DAV path - And user "Alice" has created a share inside of space "find data" with settings: + And user "Alice" has created a share inside of space "project101" with settings: | path | folderMain | | shareWith | Brian | | role | viewer | @@ -74,9 +54,9 @@ Feature: Search | spaces | - Scenario Outline: user can find hidden file + Scenario Outline: user can search hidden files Given using DAV path - And user "Alice" has created a folder ".space" in space "find data" + And user "Alice" has created a folder ".space" in space "project101" When user "Alice" searches for "*.sp*" using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "1" entries @@ -89,10 +69,10 @@ Feature: Search | spaces | - Scenario Outline: user cannot find pending share + Scenario Outline: user cannot search pending share Given user "Brian" has disabled auto-accepting And using DAV path - And user "Alice" has created a share inside of space "find data" with settings: + And user "Alice" has created a share inside of space "project101" with settings: | path | folderMain | | shareWith | Brian | | role | viewer | @@ -110,9 +90,9 @@ Feature: Search | spaces | - Scenario Outline: user cannot find declined share + Scenario Outline: user cannot search declined share Given using DAV path - And user "Alice" has created a share inside of space "find data" with settings: + And user "Alice" has created a share inside of space "project101" with settings: | path | folderMain | | shareWith | Brian | | role | viewer | @@ -131,9 +111,9 @@ Feature: Search | spaces | - Scenario Outline: user cannot find deleted folder + Scenario Outline: user cannot search deleted items Given using DAV path - And user "Alice" has removed the folder "folderMain" from space "find data" + And user "Alice" has removed the folder "folderMain" from space "project101" When user "Alice" searches for "*folderMain*" using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "0" entries @@ -144,12 +124,12 @@ Feature: Search | spaces | - Scenario Outline: user can find project space by name + Scenario Outline: user can search project space by name Given using DAV path - When user "Alice" searches for '"*find data*"' using the WebDAV API + When user "Alice" searches for '*project101*' using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "1" entries - And for user "Alice" the search result should contain space "find data" + And for user "Alice" the search result should contain space "project101" Examples: | dav-path-version | | old | @@ -159,7 +139,7 @@ Feature: Search Scenario Outline: user can search inside folder in space Given using DAV path - When user "Alice" searches for "*folder*" inside folder "/folderMain" in space "find data" using the WebDAV API + When user "Alice" searches for "*folder*" inside folder "/folderMain" in space "project101" using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "3" entries And the search result of user "Alice" should contain only these entries: @@ -177,7 +157,7 @@ Feature: Search Scenario Outline: search inside folder in shares Given using DAV path - And user "Alice" has created a share inside of space "find data" with settings: + And user "Alice" has created a share inside of space "project101" with settings: | path | folderMain | | shareWith | Brian | | role | viewer | @@ -277,16 +257,24 @@ Feature: Search Scenario Outline: search resources using different search patterns (KQL feature) Given using spaces DAV path - And user "Alice" has created a folder "subfolder" in space "find data" + And user "Alice" has created a folder "subfolder" in space "project101" When user "Alice" searches for '' using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "1" entries And the search result of user "Alice" should contain these entries: | | Examples: - | description | pattern | search-result | - | starts with | fold* | /folderMain | - | ends with | *der1 | /folderMain/SubFolder1 | - | strict search | subfolder | /subfolder | - | using patern "name:" | name:*der2 | /folderMain/SubFolder1/subFOLDER2 | - | using the pattern "name:" where the value is in quotes | name:"*der2" | /folderMain/SubFolder1/subFOLDER2 | + | pattern | search-result | description | + | fold* | /folderMain | starts with | + | *der1 | /folderMain/SubFolder1 | ends with | + | subfolder | /subfolder | exact search | + | name:*der2 | /folderMain/SubFolder1/subFOLDER2 | patern 'name:'' | + | name:"*der2" | /folderMain/SubFolder1/subFOLDER2 | pattern 'name:""' (with quotes) | + + @issue-7812 @issue-8442 + Scenario: try to search with invalid patterns + Given using spaces DAV path + And user "Alice" has uploaded file with content "test file" to "testFile.txt" + When user "Alice" searches for 'AND mediatype:document' using the WebDAV API + Then the HTTP status code should be "400" + And the value of the item "/d:error/s:message" in the response should be "error: bad request: the expression can't begin from a binary operator: 'AND'" diff --git a/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature b/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature index eb72306ea..48a8ebfd4 100644 --- a/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature +++ b/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature @@ -70,16 +70,16 @@ Feature: media type search Scenario Outline: search for file inside project space using media type Given the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API - And user "Alice" has created a space "find data" with the default quota using the Graph API - And user "Alice" has uploaded a file "filesForUpload/lorem.txt" to "/lorem.txt" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/simple.pdf" to "/simple.pdf" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/testavatar.jpg" to "/testavatar.jpg" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/testavatar.png" to "/testavatar.png" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar.gz" to "/data.tar.gz" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar" to "/data.tar" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.7z" to "/data.7z" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.rar" to "/data.rar" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar.bz2" to "/data.tar.bz2" in space "find data" + And user "Alice" has created a space "project101" with the default quota using the Graph API + And user "Alice" has uploaded a file "filesForUpload/lorem.txt" to "/lorem.txt" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/simple.pdf" to "/simple.pdf" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/testavatar.jpg" to "/testavatar.jpg" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/testavatar.png" to "/testavatar.png" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar.gz" to "/data.tar.gz" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar" to "/data.tar" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.7z" to "/data.7z" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.rar" to "/data.rar" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar.bz2" to "/data.tar.bz2" in space "project101" When user "Alice" searches for "mediatype:" using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "1" entries @@ -128,19 +128,19 @@ Feature: media type search | *bzip2* | /uploadFolder/data.tar.bz2 | - Scenario Outline: sharee searches for files inside shared space using media type + Scenario Outline: space viewer searches for files using mediatype filter Given the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API - And user "Alice" has created a space "find data" with the default quota using the Graph API - And user "Alice" has uploaded a file "filesForUpload/lorem.txt" to "/lorem.txt" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/simple.pdf" to "/simple.pdf" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/testavatar.jpg" to "/testavatar.jpg" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/testavatar.png" to "/testavatar.png" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar.gz" to "/data.tar.gz" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar" to "/data.tar" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.7z" to "/data.7z" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.rar" to "/data.rar" in space "find data" - And user "Alice" has uploaded a file "filesForUpload/data.tar.bz2" to "/data.tar.bz2" in space "find data" - And user "Alice" has shared a space "find data" with settings: + And user "Alice" has created a space "project101" with the default quota using the Graph API + And user "Alice" has uploaded a file "filesForUpload/lorem.txt" to "/lorem.txt" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/simple.pdf" to "/simple.pdf" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/testavatar.jpg" to "/testavatar.jpg" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/testavatar.png" to "/testavatar.png" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar.gz" to "/data.tar.gz" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar" to "/data.tar" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.7z" to "/data.7z" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.rar" to "/data.rar" in space "project101" + And user "Alice" has uploaded a file "filesForUpload/data.tar.bz2" to "/data.tar.bz2" in space "project101" + And user "Alice" has shared a space "project101" with settings: | shareWith | Brian | | role | viewer | When user "Brian" searches for "mediatype:" using the WebDAV API @@ -159,3 +159,51 @@ Feature: media type search | *7z* | /data.7z | | *rar* | /data.rar | | *bzip2* | /data.tar.bz2 | + + + Scenario: search files with different mediatype filter + Given user "Alice" has created folder "testFolder" + And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "lorem.txt" + And user "Alice" has uploaded file "filesForUpload/simple.odt" to "simple.odt" + And user "Alice" has uploaded file "filesForUpload/simple.pdf" to "simple.pdf" + And user "Alice" has uploaded file "filesForUpload/testavatar.jpg" to "testavatar.jpg" + And user "Alice" has uploaded file "filesForUpload/testavatar.png" to "testavatar.png" + And user "Alice" has uploaded file "filesForUpload/example.gif" to "example.gif" + And user "Alice" has uploaded file "filesForUpload/data.tar.gz" to "data.tar.gz" + And user "Alice" has uploaded file "filesForUpload/data.tar" to "data.tar" + And user "Alice" has uploaded file "filesForUpload/data.7z" to "data.7z" + And user "Alice" has uploaded file "filesForUpload/data.rar" to "data.rar" + And user "Alice" has uploaded file "filesForUpload/data.tar.bz2" to "data.tar.bz2" + When user "Alice" searches for "mediatype:folder" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "2" entries + And the search result of user "Alice" should contain these entries: + | %spaceid% | + | testFolder | + When user "Alice" searches for "mediatype:document" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "2" entries + And the search result of user "Alice" should contain these entries: + | lorem.txt | + | simple.odt | + When user "Alice" searches for "mediatype:pdf" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "1" entries + And the search result of user "Alice" should contain these entries: + | simple.pdf | + When user "Alice" searches for "mediatype:image" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "3" entries + And the search result of user "Alice" should contain these entries: + | testavatar.jpg | + | testavatar.png | + | example.gif | + When user "Alice" searches for "mediatype:archive" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "5" entries + And the search result of user "Alice" should contain these entries: + | data.tar.gz | + | data.tar | + | data.7z | + | data.rar | + | data.tar.bz2 | diff --git a/tests/acceptance/features/apiSharingNg/deletePermissions.feature b/tests/acceptance/features/apiSharingNg/deletePermissions.feature index 222514e4c..ffc90cb12 100644 --- a/tests/acceptance/features/apiSharingNg/deletePermissions.feature +++ b/tests/acceptance/features/apiSharingNg/deletePermissions.feature @@ -13,23 +13,22 @@ Feature: Remove access to a drive item Given user "Alice" has created folder "FolderToShare" And user "Alice" has uploaded file "filesForUpload/textfile.txt" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | | shareType | user | | permissionsRole | | - When user "Alice" removes the share permission of user "Brian" from "" of space "Personal" using the Graph API + When user "Alice" removes the share permission of user "Brian" from resource "" of space "Personal" using the Graph API Then the HTTP status code should be "204" And for user "Brian" the space "Shares" should not contain these entries: | | Examples: - | permissionsRole | resource-type | path | - | Viewer | file | textfile.txt | - | File Editor | file | textfile.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissionsRole | path | + | Viewer | textfile.txt | + | File Editor | textfile.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: user removes access to resource inside of a project space in the user share @@ -38,23 +37,22 @@ Feature: Remove access to a drive item And user "Alice" has created a folder "FolderToShare" in space "NewSpace" And user "Alice" has uploaded a file inside space "NewSpace" with content "some content" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | | | resource | | | space | NewSpace | | sharee | Brian | | shareType | user | | permissionsRole | | - When user "Alice" removes the share permission of user "Brian" from "" of space "NewSpace" using the Graph API + When user "Alice" removes the share permission of user "Brian" from resource "" of space "NewSpace" using the Graph API Then the HTTP status code should be "204" And for user "Brian" the space "Shares" should not contain these entries: | | Examples: - | permissionsRole | resource-type | path | - | Viewer | file | textfile.txt | - | File Editor | file | textfile.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissionsRole | path | + | Viewer | textfile.txt | + | File Editor | textfile.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: user removes access to a resource in a group share @@ -64,23 +62,22 @@ Feature: Remove access to a drive item And user "Alice" has created folder "FolderToShare" And user "Alice" has uploaded file "filesForUpload/textfile.txt" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | | | resource | | | space | Personal | | sharee | group1 | | shareType | group | | permissionsRole | | - When user "Alice" removes the share permission of group "group1" from "" of space "Personal" using the Graph API + When user "Alice" removes the share permission of group "group1" from resource "" of space "Personal" using the Graph API Then the HTTP status code should be "204" And for user "Brian" the space "Shares" should not contain these entries: | | Examples: - | permissionsRole | resource-type | path | - | Viewer | file | textfile.txt | - | File Editor | file | textfile.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissionsRole | path | + | Viewer | textfile.txt | + | File Editor | textfile.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: user removes access to a resource inside of a project space in group share @@ -92,29 +89,27 @@ Feature: Remove access to a drive item And user "Brian" has been added to group "group1" And user "Alice" has been added to group "group1" And user "Alice" has sent the following share invitation: - | resourceType | | | resource | | | space | NewSpace | | sharee | group1 | | shareType | group | | permissionsRole | | - When user "Alice" removes the share permission of group "group1" from "" of space "NewSpace" using the Graph API + When user "Alice" removes the share permission of group "group1" from resource "" of space "NewSpace" using the Graph API Then the HTTP status code should be "204" And for user "Brian" the space "Shares" should not contain these entries: | | Examples: - | permissionsRole | resource-type | path | - | Viewer | file | textfile.txt | - | File Editor | file | textfile.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissionsRole | path | + | Viewer | textfile.txt | + | File Editor | textfile.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: user removes access to a folder in link share Given user "Alice" has created folder "FolderToShare" And user "Alice" has created the following link share: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | permissionsRole | | @@ -133,7 +128,6 @@ Feature: Remove access to a drive item Scenario Outline: user removes access to a file in link share Given user "Alice" has uploaded file "filesForUpload/textfile.txt" to "textfile.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile.txt | | space | Personal | | permissionsRole | | @@ -152,7 +146,6 @@ Feature: Remove access to a drive item And user "Alice" has created a space "NewSpace" with the default quota using the Graph API And user "Alice" has created a folder "FolderToShare" in space "NewSpace" And user "Alice" has created the following link share: - | resourceType | folder | | resource | FolderToShare | | space | NewSpace | | permissionsRole | | @@ -173,7 +166,6 @@ Feature: Remove access to a drive item And user "Alice" has created a space "NewSpace" with the default quota using the Graph API And user "Alice" has uploaded a file inside space "NewSpace" with content "some content" to "textfile.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile.txt | | space | NewSpace | | permissionsRole | | diff --git a/tests/acceptance/features/apiSharingNg/linkShare.feature b/tests/acceptance/features/apiSharingNg/linkShare.feature index b7259c17a..23a419d97 100644 --- a/tests/acceptance/features/apiSharingNg/linkShare.feature +++ b/tests/acceptance/features/apiSharingNg/linkShare.feature @@ -10,7 +10,6 @@ Feature: Create a share link for a resource Scenario Outline: create a link share of a folder Given user "Alice" has created folder "folder" When user "Alice" creates the following link share using the Graph API: - | resourceType | folder | | resource | folder | | space | Personal | | permissionsRole | | @@ -82,7 +81,6 @@ Feature: Create a share link for a resource Scenario Outline: create a link share of a file Given user "Alice" has uploaded file with content "other data" to "textfile1.txt" When user "Alice" creates the following link share using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | | @@ -152,7 +150,6 @@ Feature: Create a share link for a resource Scenario Outline: create a link share of a folder with display name and expiry date Given user "Alice" has created folder "folder" When user "Alice" creates the following link share using the Graph API: - | resourceType | folder | | resource | folder | | space | Personal | | permissionsRole | | @@ -231,7 +228,6 @@ Feature: Create a share link for a resource Scenario Outline: create a link share of a file with display name and expiry date Given user "Alice" has uploaded file with content "other data" to "textfile1.txt" When user "Alice" creates the following link share using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | | @@ -311,7 +307,6 @@ Feature: Create a share link for a resource | OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD | false | And user "Alice" has uploaded file with content "other data" to "textfile1.txt" When user "Alice" creates the following link share using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | | @@ -380,13 +375,11 @@ Feature: Create a share link for a resource Scenario Outline: update role of a file's link share Given user "Alice" has uploaded file with content "other data" to "textfile1.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | | | password | %public% | When user "Alice" updates the last public link share using the Graph API with - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | | @@ -460,14 +453,12 @@ Feature: Create a share link for a resource Scenario: update expiration date of a file's link share Given user "Alice" has uploaded file with content "other data" to "textfile1.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | view | | password | %public% | | expirationDateTime | 2200-07-15T14:00:00.000Z | When user "Alice" updates the last public link share using the Graph API with - | resourceType | file | | resource | textfile1.txt | | space | Personal | | expirationDateTime | 2201-07-15T14:00:00.000Z | @@ -534,12 +525,10 @@ Feature: Create a share link for a resource | OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD | false | And user "Alice" has uploaded file with content "other data" to "textfile1.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | view | When user "Alice" sets the following password for the last link share using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | password | %public% | @@ -564,13 +553,11 @@ Feature: Create a share link for a resource Scenario: update password of a file's link share Given user "Alice" has uploaded file with content "other data" to "textfile1.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | permissionsRole | view | | password | $heLlo*1234* | When user "Alice" sets the following password for the last link share using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | password | %public% | @@ -598,7 +585,6 @@ Feature: Create a share link for a resource Given the config "OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" has been set to path "config/drone/banned-password-list.txt" And user "Alice" has uploaded file with content "other data" to "text.txt" When user "Alice" creates the following link share using the Graph API: - | resourceType | file | | resource | text.txt | | space | Personal | | permissionsRole | view | @@ -645,13 +631,11 @@ Feature: Create a share link for a resource Given the config "OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" has been set to path "config/drone/banned-password-list.txt" And user "Alice" has uploaded file with content "other data" to "text.txt" And user "Alice" has created the following link share: - | resourceType | file | | resource | text.txt | | space | Personal | | permissionsRole | view | | password | %public% | When user "Alice" sets the following password for the last link share using the Graph API: - | resourceType | file | | resource | text.txt | | space | Personal | | permissionsRole | view | diff --git a/tests/acceptance/features/apiSharingNg/listPermissions.feature b/tests/acceptance/features/apiSharingNg/listPermissions.feature index e15b85720..e2ee6aa77 100644 --- a/tests/acceptance/features/apiSharingNg/listPermissions.feature +++ b/tests/acceptance/features/apiSharingNg/listPermissions.feature @@ -48,6 +48,8 @@ Feature: List a sharing permissions }, "@libre.graph.permissions.roles.allowedValues": { "type": "array", + "minItems": 4, + "maxItems": 4, "items": [ { "type": "object", @@ -188,76 +190,6 @@ Feature: List a sharing permissions ] } } - }, - { - "type": "object", - "required": [ - "@libre.graph.weight", - "description", - "displayName", - "id" - ], - "properties": { - "@libre.graph.weight": { - "type": "integer", - "enum": [ - 5 - ] - }, - "description": { - "type": "string", - "enum": [ - "Grants co-owner permissions on a resource" - ] - }, - "displayName": { - "type": "string", - "enum": [ - "Co Owner" - ] - }, - "id": { - "type": "string", - "enum": [ - "3a4ba8e9-6a0d-4235-9140-0e7a34007abe" - ] - } - } - }, - { - "type": "object", - "required": [ - "@libre.graph.weight", - "description", - "displayName", - "id" - ], - "properties": { - "@libre.graph.weight": { - "type": "integer", - "enum": [ - 6 - ] - }, - "description": { - "type": "string", - "enum": [ - "Grants manager permissions on a resource. Semantically equivalent to co-owner" - ] - }, - "displayName": { - "type": "string", - "enum": [ - "Manager" - ] - }, - "id": { - "type": "string", - "enum": [ - "312c0871-5ef7-4b3a-85b6-0e4074c64049" - ] - } - } } ] } diff --git a/tests/acceptance/features/apiSharingNg/shareInvitations.feature b/tests/acceptance/features/apiSharingNg/shareInvitations.feature index 2e2af55dc..aac62a8c6 100644 --- a/tests/acceptance/features/apiSharingNg/shareInvitations.feature +++ b/tests/acceptance/features/apiSharingNg/shareInvitations.feature @@ -16,7 +16,6 @@ Feature: Send a sharing invitations Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | @@ -88,12 +87,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send share invitation to group with different roles @@ -106,7 +105,6 @@ Feature: Send a sharing invitations And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1 | @@ -180,18 +178,17 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send share invitation for a file to user with different permissions Given user "Alice" has uploaded file with content "to share" to "textfile1.txt" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | sharee | Brian | @@ -283,7 +280,6 @@ Feature: Send a sharing invitations Scenario Outline: send share invitation for a folder to user with different permissions Given user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | sharee | Brian | @@ -384,7 +380,6 @@ Feature: Send a sharing invitations | Carol | grp1 | And user "Alice" has uploaded file with content "to share" to "textfile1.txt" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | file | | resource | textfile1.txt | | space | Personal | | sharee | grp1 | @@ -482,7 +477,6 @@ Feature: Send a sharing invitations | Carol | grp1 | And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | sharee | grp1 | @@ -578,7 +572,6 @@ Feature: Send a sharing invitations Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | @@ -656,12 +649,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send share invitation with expiration date to group with different roles @@ -674,7 +667,6 @@ Feature: Send a sharing invitations And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1 | @@ -752,12 +744,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | @issue-7962 Scenario Outline: send share invitation to disabled user @@ -765,7 +757,6 @@ Feature: Send a sharing invitations And user "Alice" has created folder "FolderToShare" And the user "Admin" has disabled user "Brian" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | @@ -835,12 +826,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send sharing invitation to a deleted group with different roles @@ -854,7 +845,6 @@ Feature: Send a sharing invitations And user "Alice" has created folder "FolderToShare" And the administrator has deleted group "grp1" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1 | @@ -894,12 +884,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send share invitation to deleted user @@ -907,7 +897,6 @@ Feature: Send a sharing invitations And user "Alice" has created folder "FolderToShare" And the user "Admin" has deleted a user "Brian" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | @@ -945,12 +934,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: try to send sharing invitation to multiple groups @@ -968,7 +957,6 @@ Feature: Send a sharing invitations And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1, grp2 | @@ -1008,14 +996,14 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Co Owner | folder | FolderToShare | - | Uploader | folder | FolderToShare | - | Manager | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Co Owner | FolderToShare | + | Uploader | FolderToShare | + | Manager | FolderToShare | Scenario Outline: try to send sharing invitation to user and group at once @@ -1031,7 +1019,6 @@ Feature: Send a sharing invitations And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1, Bob | @@ -1071,19 +1058,18 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send sharing invitation to non-existing group Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | nonExistentGroup | @@ -1123,12 +1109,12 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send sharing invitation to already shared group @@ -1141,14 +1127,12 @@ Feature: Send a sharing invitations And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" And user "Alice" has sent the following share invitation: - | resourceType | | | resource | | | space | Personal | | sharee | grp1 | | shareType | group | | permissionsRole | | When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | grp1 | @@ -1186,19 +1170,18 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Viewer | file | /textfile1.txt | - | File Editor | file | /textfile1.txt | - | Viewer | folder | FolderToShare | - | Editor | folder | FolderToShare | - | Uploader | folder | FolderToShare | + | permissions-role | path | + | Viewer | /textfile1.txt | + | File Editor | /textfile1.txt | + | Viewer | FolderToShare | + | Editor | FolderToShare | + | Uploader | FolderToShare | Scenario Outline: send share invitation to wrong user id Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | shareeId | a4c0c83e-ae24-4870-93c3-fcaf2a2228f7 | @@ -1236,21 +1219,20 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation with empty user id Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | shareeId | | - | shareType | user | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | shareeId | | + | shareType | user | + | permissionsRole | Viewer | Then the HTTP status code should be "400" And the JSON data of the response should match """ @@ -1283,21 +1265,20 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation to user with wrong recipient type Given user "Alice" has uploaded file with content "to share" to "textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | sharee | Brian | - | shareType | wrongShareType | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | wrongShareType | + | permissionsRole | Viewer | Then the HTTP status code should be "400" And the JSON data of the response should match """ @@ -1330,9 +1311,9 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation to group with wrong recipient type @@ -1345,12 +1326,11 @@ Feature: Send a sharing invitations | Brian | grp1 | | Carol | grp1 | When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | sharee | grp1 | - | shareType | wrongShareType | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | grp1 | + | shareType | wrongShareType | + | permissionsRole | Viewer | Then the HTTP status code should be "400" And the JSON data of the response should match """ @@ -1383,21 +1363,20 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation to user with empty recipient type Given user "Alice" has uploaded file with content "to share" to "textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | sharee | Brian | - | shareType | | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | | + | permissionsRole | Viewer | Then the HTTP status code should be "400" And the JSON data of the response should match """ @@ -1430,9 +1409,9 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation to group with empty recipient type @@ -1445,12 +1424,11 @@ Feature: Send a sharing invitations | Brian | grp1 | | Carol | grp1 | When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | sharee | grp1 | - | shareType | | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | grp1 | + | shareType | | + | permissionsRole | Viewer | Then the HTTP status code should be "400" And the JSON data of the response should match """ @@ -1483,16 +1461,15 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: try to share a resource with invalid roles Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" And user "Alice" has created folder "FolderToShare" When user "Alice" sends the following share invitation using the Graph API: - | resourceType | | | resource | | | space | Personal | | sharee | Brian | @@ -1532,15 +1509,15 @@ Feature: Send a sharing invitations } """ Examples: - | permissions-role | resource-type | path | - | Co Owner | file | /textfile1.txt | - | Manager | file | /textfile1.txt | - | Space Viewer | file | /textfile1.txt | - | Space Editor | file | /textfile1.txt | - | Co Owner | folder | FolderToShare | - | Manager | folder | FolderToShare | - | Space Viewer | folder | FolderToShare | - | Space Editor | folder | FolderToShare | + | permissions-role | path | + | Co Owner | /textfile1.txt | + | Manager | /textfile1.txt | + | Space Viewer | /textfile1.txt | + | Space Editor | /textfile1.txt | + | Co Owner | FolderToShare | + | Manager | FolderToShare | + | Space Viewer | FolderToShare | + | Space Editor | FolderToShare | Scenario Outline: try to share a file with invalid roles @@ -1594,19 +1571,17 @@ Feature: Send a sharing invitations Given user "Alice" has uploaded file with content "to share" to "textfile1.txt" And user "Alice" has created folder "FolderToShare" And user "Alice" has sent the following share invitation: - | resourceType | | - | resource | | - | space | Personal | - | sharee | Brian | - | shareType | user | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | When user "Alice" tries to send the following share invitation using the Graph API: - | resourceType | | - | resource | | - | space | Personal | - | sharee | Brian | - | shareType | user | - | permissionsRole | Viewer | + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | Then the HTTP status code should be "409" And the JSON data of the response should match """ @@ -1637,9 +1612,9 @@ Feature: Send a sharing invitations } """ Examples: - | resource-type | path | - | file | /textfile1.txt | - | folder | FolderToShare | + | path | + | /textfile1.txt | + | FolderToShare | Scenario Outline: send share invitation for project space to user with different roles diff --git a/tests/acceptance/features/apiSharingNg/sharedByMe.feature b/tests/acceptance/features/apiSharingNg/sharedByMe.feature index b2e368917..1f30c008f 100644 --- a/tests/acceptance/features/apiSharingNg/sharedByMe.feature +++ b/tests/acceptance/features/apiSharingNg/sharedByMe.feature @@ -15,7 +15,6 @@ Feature: resources shared by user Scenario: user lists the shared file from personal space Given user "Alice" has uploaded file with content "hello world" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | textfile.txt | | space | Personal | | sharee | Brian | @@ -140,7 +139,6 @@ Feature: resources shared by user Given user "Alice" has created folder "FolderToShare" And user "Alice" has uploaded file with content "hello world" to "FolderToShare/textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | FolderToShare/textfile.txt | | space | Personal | | sharee | Brian | @@ -264,7 +262,6 @@ Feature: resources shared by user Scenario: user lists the shared folder from personal space Given user "Alice" has created folder "FolderToShare" And user "Alice" has sent the following share invitation: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | sharee | Brian | @@ -382,14 +379,12 @@ Feature: resources shared by user Given user "Alice" has created folder "FolderToShare" And user "Alice" has uploaded file with content "hello world" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | textfile.txt | | space | Personal | | sharee | Brian | | shareType | user | | permissionsRole | Viewer | And user "Alice" has sent the following share invitation: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | sharee | Brian | @@ -620,7 +615,6 @@ Feature: resources shared by user And user "Alice" has created a space "NewSpace" with the default quota using the Graph API And user "Alice" has uploaded a file inside space "NewSpace" with content "hello world" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | textfile.txt | | space | NewSpace | | sharee | Brian | @@ -748,7 +742,6 @@ Feature: resources shared by user And user "Alice" has created a folder "FolderToShare" in space "NewSpace" And user "Alice" has uploaded a file inside space "NewSpace" with content "hello world" to "FolderToShare/textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | FolderToShare/textfile.txt | | space | NewSpace | | sharee | Brian | @@ -875,7 +868,6 @@ Feature: resources shared by user And user "Alice" has created a space "NewSpace" with the default quota using the Graph API And user "Alice" has created a folder "FolderToShare" in space "NewSpace" And user "Alice" has sent the following share invitation: - | resourceType | folder | | resource | FolderToShare | | space | NewSpace | | sharee | Brian | @@ -996,14 +988,12 @@ Feature: resources shared by user And user "Alice" has created a folder "FolderToShare" in space "NewSpace" And user "Alice" has uploaded a file inside space "NewSpace" with content "hello world" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | textfile.txt | | space | NewSpace | | sharee | Brian | | shareType | user | | permissionsRole | Viewer | And user "Alice" has sent the following share invitation: - | resourceType | folder | | resource | FolderToShare | | space | NewSpace | | sharee | Brian | @@ -1233,14 +1223,12 @@ Feature: resources shared by user And user "Alice" has created folder "FolderToShare" And user "Alice" has uploaded file with content "hello world" to "textfile.txt" And user "Alice" has sent the following share invitation: - | resourceType | file | | resource | textfile.txt | | space | Personal | | sharee | grp1 | | shareType | group | | permissionsRole | Viewer | And user "Alice" has sent the following share invitation: - | resourceType | folder | | resource | FolderToShare | | space | Personal | | sharee | grp1 | diff --git a/tests/acceptance/features/apiSharingNg/sharedWithMe.feature b/tests/acceptance/features/apiSharingNg/sharedWithMe.feature old mode 100644 new mode 100755 index 7078597f7..b0bee3d76 --- a/tests/acceptance/features/apiSharingNg/sharedWithMe.feature +++ b/tests/acceptance/features/apiSharingNg/sharedWithMe.feature @@ -35,6 +35,8 @@ Feature: an user gets the resources shared to them "items": { "type": "object", "required": [ + "@UI.Hidden", + "@client.synchronize", "createdBy", "eTag", "file", @@ -42,9 +44,18 @@ Feature: an user gets the resources shared to them "lastModifiedDateTime", "name", "parentReference", - "remoteItem" + "remoteItem", + "size" ], "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [true] + }, "createdBy": { "type": "object", "required": [ @@ -69,7 +80,7 @@ Feature: an user gets the resources shared to them }, "eTag": { "type": "string", - "pattern": "%eTag%" + "pattern": "%etag_pattern%" }, "file": { "type": "object", @@ -95,16 +106,21 @@ Feature: an user gets the resources shared to them "type": "object", "required": [ "driveId", - "driveType" + "driveType", + "id" ], "properties": { "driveId": { "type": "string", "pattern": "^%space_id_pattern%$" }, - "driveType" : { + "driveType": { "type": "string", "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" } } }, @@ -151,7 +167,7 @@ Feature: an user gets the resources shared to them }, "eTag": { "type": "string", - "pattern": "%eTag%" + "pattern": "%etag_pattern%" }, "file": { "type": "object", @@ -175,107 +191,121 @@ Feature: an user gets the resources shared to them "textfile0.txt" ] }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, "permissions": { "type": "array", "items": [ { "type": "object", "required": [ - "grantedToV2", - "id", - "invitation" - ], - "properties": { - "id": { - "type": "string" - }, - "grantedToV2": { - "type": "object", - "required": [ - "user" - ], - "properties": { - "user": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "displayName", - "id" - ] - } - } - }, - "invitation": { - "type": "object", - "properties": { - "invitedBy": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "displayName", - "id" - ] - } - }, - "required": [ - "user" - ] - } - }, - "required": [ - "invitedBy" - ] - }, - "@libre.graph.permissions.actions": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - }, - "roles": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - } - } - } - ] - }, - "size": { - "type": "number", - "enum": [ - 11 - ] - } - } - } - } - } - } - } - } + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + }, + "required": [ + "displayName", + "id" + ] + } + }, + "required": [ + "user" + ] + } + }, + "required": [ + "invitedBy" + ] + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + }, + "size": { + "type": "number", + "enum": [11] + } + } + } + } + } + } """ @@ -302,6 +332,8 @@ Feature: an user gets the resources shared to them "items": { "type": "object", "required": [ + "@UI.Hidden", + "@client.synchronize", "createdBy", "eTag", "folder", @@ -312,6 +344,14 @@ Feature: an user gets the resources shared to them "remoteItem" ], "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [true] + }, "createdBy": { "type": "object", "required": [ @@ -336,7 +376,7 @@ Feature: an user gets the resources shared to them }, "eTag": { "type": "string", - "pattern": "%eTag%" + "pattern": "%etag_pattern%" }, "folder": { "type": "object", @@ -357,16 +397,21 @@ Feature: an user gets the resources shared to them "type": "object", "required": [ "driveId", - "driveType" + "driveType", + "id" ], "properties": { "driveId": { "type": "string", "pattern": "^%space_id_pattern%$" }, - "driveType" : { + "driveType": { "type": "string", "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" } } }, @@ -412,7 +457,7 @@ Feature: an user gets the resources shared to them }, "eTag": { "type": "string", - "pattern": "%eTag%" + "pattern": "%etag_pattern%" }, "file": { "type": "object", @@ -422,7 +467,7 @@ Feature: an user gets the resources shared to them "properties": { "mimeType": { "type": "string", - "pattern": "text/plain" + "enum": ["text/plain"] } } }, @@ -436,99 +481,2443 @@ Feature: an user gets the resources shared to them "folder" ] }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, "permissions": { "type": "array", "items": [ { "type": "object", "required": [ - "grantedToV2", - "id", - "invitation" - ], - "properties": { - "id": { - "type": "string" - }, - "grantedToV2": { - "type": "object", - "required": [ - "user" - ], - "properties": { - "user": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "displayName", - "id" - ] - } - } - }, - "invitation": { - "type": "object", - "properties": { - "invitedBy": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "displayName", - "id" - ] - } - }, - "required": [ - "user" - ] - } - }, - "required": [ - "invitedBy" - ] - }, - "@libre.graph.permissions.actions": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - }, - "roles": { - "type": "array", - "items": [ - { - "type": "string" - } - ] - } - } - } - ] - } - } - } - } - } - } - } - } + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + }, + "required": [ + "displayName", + "id" + ] + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + }, + "required": [ + "displayName", + "id" + ] + } + }, + "required": [ + "user" + ] + } + }, + "required": [ + "invitedBy" + ] + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + } + } + } + } + } + } + """ + + + Scenario: sharer shares a file to a group and to a user who is in the shared group + Given the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has uploaded file with content "hello" to "textfile0.txt" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "createdBy": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "textfile0.txt" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "createdBy": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "id", + "displayName" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": [ + "Alice Hansen" + ] + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "textfile0.txt" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "group" + ], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + }, + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + }, + "size": { + "type": "number", + "enum": [ + 5 + ] + } + } + }, + "size": { + "type": "number", + "enum": [ + 5 + ] + } + } + } + } + } + } + """ + + @issues-8314 + Scenario: sharer shares a file in project space to a group and to a user who is in the shared group file + Given using spaces DAV path + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "new-space" with the default quota using the Graph API + And user "Alice" has uploaded a file inside space "new-space" with content "hello world" to "textfile0.txt" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | new-space | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "textfile0.txt" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "textfile0.txt" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "group" + ], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + }, + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + }, + "size": { + "type": "number", + "enum": [ + 11 + ] + } + } + }, + "size": { + "type": "number", + "enum": [ + 11 + ] + } + } + } + } + } + } + """ + + + Scenario: sharer shares a folder to a group and to a user who is in the shared group folder + Given the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created folder "folder" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "createdBy": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "folder": {}, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "folder" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "createdBy": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "id", + "displayName" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": [ + "Alice Hansen" + ] + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": {}, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "folder" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "group" + ], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + }, + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + } + } + } + } + } + } + } + """ + + @issues-8314 + Scenario: sharer shares a folder in project space to a group and to a user who is in the shared group + Given using spaces DAV path + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "new-space" with the default quota using the Graph API + And user "Alice" has created a group "grp1" using the Graph API + And user "Alice" has created a folder "folder" in space "new-space" + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | new-space | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "folder": {}, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "folder" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "folder": {}, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "folder" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "group" + ], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + }, + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": [ + "invitedBy" + ], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + } + } + } + } + } + } + } + """ + + + Scenario: user lists file shared with them in a group from sharer's personal space + Given the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has uploaded file with content "hello" to "textfile0.txt" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": [ + "textfile0.txt" + ] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": [ + "id", + "displayName" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile0.txt"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + }, + "size": { + "type": "number", + "enum": [5] + } + } + }, + "size": { + "type": "number", + "enum": [5] + } + } + } + } + } + } + """ + + + Scenario: user lists file shared with them in a group from sharer's project space + Given using spaces DAV path + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "new-space" with the default quota using the Graph API + And user "Alice" has uploaded a file inside space "new-space" with content "hello world" to "textfile0.txt" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | textfile0.txt | + | space | new-space | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile0.txt"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile0.txt"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + }, + "size": { + "type": "number", + "enum": [11] + } + } + }, + "size": { + "type": "number", + "enum": [11] + } + } + } + } + } + } + """ + + + Scenario: user lists folder shared with them in a group from sharer's personal space + Given using spaces DAV path + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created folder "folder" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folder"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id","displayName"], + "properties": { + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folder"] + }, + "parentReference": { + "type": "object", + "required": ["driveId","driveType"], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + } + } + } + } + } + } + """ + + + Scenario: user lists folder shared with them in a group from sharer's project space + Given using spaces DAV path + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "new-space" with the default quota using the Graph API + And user "Alice" has created a group "grp1" using the Graph API + And user "Alice" has created a folder "folder" in space "new-space" + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following share invitation: + | resource | folder | + | space | new-space | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden":{ + "type": "boolean", + "enum": [false] + }, + "@client.synchronize":{ + "type": "boolean", + "enum": [true] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folder"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType" : { + "type": "string", + "enum": ["virtual"] + }, + "id" : { + "type": "string", + "pattern": "%space_id_pattern%" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folder"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "%space_id_pattern%" + }, + "driveType" : { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties":{ + "group": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + ] + } + } + } + } + } + } + } + } """ diff --git a/tests/acceptance/features/apiSharingNg/sharedWithMeSyncDisabled.feature b/tests/acceptance/features/apiSharingNg/sharedWithMeSyncDisabled.feature new file mode 100644 index 000000000..2e2a8ad43 --- /dev/null +++ b/tests/acceptance/features/apiSharingNg/sharedWithMeSyncDisabled.feature @@ -0,0 +1,1495 @@ +Feature: listing sharedWithMe when auto-sync is disabled + As a user + I want to get resources shared with me when auto-sync is disabled + So that I can know about what resources I have access to + + https://owncloud.dev/libre-graph-api/#/me.drive/ListSharedWithMe + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | Alice | + | Brian | + + + Scenario: user lists the file shared with them when auto-sync is disabled + Given user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType", "id"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + }, + "size": { + "type": "number", + "enum": [8] + } + } + }, + "size": { + "type": "number", + "enum": [8] + } + } + } + } + } + } + """ + + + Scenario: user lists the folder shared with them when auto-sync is disabled + Given user "Alice" has created folder "folderToShare" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | folderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType", "id"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + } + } + } + } + } + } + } + } + """ + + + Scenario: group member lists the file shared with them when auto-sync is disabled + And user "Carol" has been created with default attributes and without skeleton files + And group "grp1" has been created + And the following users have been added to the following groups + | username | groupname | + | Brian | grp1 | + | Carol | grp1 | + Given user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType", "id"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties": { + "group": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%group_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + }, + "size": { + "type": "number", + "enum": [8] + } + } + }, + "size": { + "type": "number", + "enum": [8] + } + } + } + } + } + } + """ + + + Scenario: group member lists the folder shared with them when auto-sync is disabled + And user "Carol" has been created with default attributes and without skeleton files + And group "grp1" has been created + And the following users have been added to the following groups + | username | groupname | + | Brian | grp1 | + | Carol | grp1 | + Given user "Alice" has created folder "folderToShare" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | folderToShare | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType", "id"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "createdBy", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["personal"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["grp1"] + }, + "id": { + "type": "string", + "pattern": "^%group_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + } + } + } + } + } + } + } + } + """ + + + Scenario: user lists file shares shared with them from project-space when auto-sync is disabled + Given using spaces DAV path + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "projectSpace" with the default quota using the Graph API + And user "Alice" has uploaded a file inside space "projectSpace" with content "to share" to "textfile.txt" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | textfile.txt | + | space | projectSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem", + "size" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "file", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions", + "size" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { + "type": "string", + "enum": ["text/plain"] + } + } + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["textfile.txt"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + }, + "size": { + "type": "number", + "enum": [8] + } + } + }, + "size": { + "type": "number", + "enum": [8] + } + } + } + } + } + } + """ + + + Scenario: user lists folder shares shared with them from project-space when auto-sync is disabled + Given using spaces DAV path + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And user "Alice" has created a space "projectSpace" with the default quota using the Graph API + And user "Alice" has created a folder "folderToShare" in space "projectSpace" + And user "Brian" has disabled the auto-sync share + And user "Alice" has sent the following share invitation: + | resource | folderToShare | + | space | projectSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "required": [ + "@UI.Hidden", + "@client.synchronize", + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "remoteItem" + ], + "properties": { + "@UI.Hidden": { + "type": "boolean", + "enum": [false] + }, + "@client.synchronize": { + "type": "boolean", + "enum": [false] + }, + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "folder": { + "type": "object" + }, + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType", + "id" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%space_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["virtual"] + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + } + } + }, + "remoteItem": { + "type": "object", + "required": [ + "eTag", + "folder", + "id", + "lastModifiedDateTime", + "name", + "parentReference", + "permissions" + ], + "properties": { + "eTag": { + "type": "string", + "pattern": "%etag_pattern%" + }, + "id": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "name": { + "type": "string", + "enum": ["folderToShare"] + }, + "parentReference": { + "type": "object", + "required": [ + "driveId", + "driveType" + ], + "properties": { + "driveId": { + "type": "string", + "pattern": "^%file_id_pattern%$" + }, + "driveType": { + "type": "string", + "enum": ["project"] + } + } + }, + "permissions": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "invitation", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + ] + } + } + } + } + } + } + } + } + """ diff --git a/tests/acceptance/features/apiSharingNg/updateShareInvitations.feature b/tests/acceptance/features/apiSharingNg/updateShareInvitations.feature new file mode 100644 index 000000000..0ba82e457 --- /dev/null +++ b/tests/acceptance/features/apiSharingNg/updateShareInvitations.feature @@ -0,0 +1,160 @@ +Feature: Update permission of a share + As a user + I want to update resources shared with me + So that I can have more control over my shares and manage it + https://owncloud.dev/libre-graph-api/#/drives.permissions/UpdatePermission + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | Alice | + | Brian | + + + Scenario: user updates expiration date of a share + Given user "Alice" has uploaded file with content "hello world" to "testfile.txt" + And user "Alice" has sent the following share invitation: + | resource | testfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer | + | expirationDateTime | 2025-07-15T14:00:00Z | + When user "Alice" updates the last share with the following using the Graph API: + | space | Personal | + | resource | testfile.txt | + | expirationDateTime | 2200-07-15T14:00:00Z | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "expirationDateTime", + "grantedToV2", + "id", + "roles" + ], + "properties": { + "expirationDateTime": { + "type": "string", + "enum": ["2200-07-15T14:00:00Z"] + }, + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + """ + + + Scenario Outline: user updates role of a share + Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" + And user "Alice" has created folder "FolderToShare" + And user "Alice" has sent the following share invitation: + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last share with the following using the Graph API: + | permissionsRole | | + | space | Personal | + | resource | | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": [ + "grantedToV2", + "id", + "roles" + ], + "properties": { + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties":{ + "user": { + "type": "object", + "required": [ + "displayName", + "id" + ], + "properties": { + "displayName": { + "type": "string", + "enum": ["Brian Murphy"] + }, + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + }, + "id": { + "type": "string", + "pattern": "^%permissions_id_pattern%$" + }, + "roles": { + "type": "array", + "items": [ + { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + ] + } + } + } + """ + Examples: + | previous-permissions-role | path | new-permissions-role | + | Viewer | textfile1.txt | File Editor | + | File Editor | textfile1.txt | Viewer | + | Viewer | FolderToShare | Uploader | + | Viewer | FolderToShare | Editor | + | Editor | FolderToShare | Viewer | + | Editor | FolderToShare | Uploader | + | Uploader | FolderToShare | Editor | + | Uploader | FolderToShare | Viewer | \ No newline at end of file diff --git a/tests/acceptance/features/bootstrap/FeatureContext.php b/tests/acceptance/features/bootstrap/FeatureContext.php index 53571a961..0293466e0 100644 --- a/tests/acceptance/features/bootstrap/FeatureContext.php +++ b/tests/acceptance/features/bootstrap/FeatureContext.php @@ -1218,6 +1218,7 @@ class FeatureContext extends BehatVariablesContext { */ public function userSendsHTTPMethodToUrl(string $user, string $verb, string $url): void { $user = $this->getActualUsername($user); + $url = $this->substituteInLineCodes($url, $user); $this->setResponse($this->sendingToWithDirectUrl($user, $verb, $url, null)); } @@ -2746,30 +2747,6 @@ class FeatureContext extends BehatVariablesContext { ], "parameter" => [] ], - [ - "code" => "%group_id_pattern%", - "function" => [ - __NAMESPACE__ . '\TestHelpers\GraphHelper', - "getUUIDv4Regex" - ], - "parameter" => [] - ], - [ - "code" => "%space_id_pattern%", - "function" => [ - __NAMESPACE__ . '\TestHelpers\GraphHelper', - "getSpaceIdRegex" - ], - "parameter" => [] - ], - [ - "code" => "%user_id_pattern%", - "function" => [ - __NAMESPACE__ . '\TestHelpers\GraphHelper', - "getUUIDv4Regex" - ], - "parameter" => [] - ], [ "code" => "%user_id%", "function" => [ @@ -2784,6 +2761,22 @@ class FeatureContext extends BehatVariablesContext { ], "parameter" => [$group] ], + [ + "code" => "%user_id_pattern%", + "function" => [ + __NAMESPACE__ . '\TestHelpers\GraphHelper', + "getUUIDv4Regex" + ], + "parameter" => [] + ], + [ + "code" => "%group_id_pattern%", + "function" => [ + __NAMESPACE__ . '\TestHelpers\GraphHelper', + "getUUIDv4Regex" + ], + "parameter" => [] + ], [ "code" => "%role_id_pattern%", "function" => [ @@ -2808,6 +2801,14 @@ class FeatureContext extends BehatVariablesContext { ], "parameter" => [] ], + [ + "code" => "%space_id_pattern%", + "function" => [ + __NAMESPACE__ . '\TestHelpers\GraphHelper', + "getSpaceIdRegex" + ], + "parameter" => [] + ], [ "code" => "%share_id_pattern%", "function" => [ @@ -2817,7 +2818,7 @@ class FeatureContext extends BehatVariablesContext { "parameter" => [] ], [ - "code" => "%eTag%", + "code" => "%etag_pattern%", "function" => [ __NAMESPACE__ . '\TestHelpers\GraphHelper', "getEtagRegex" @@ -2881,6 +2882,20 @@ class FeatureContext extends BehatVariablesContext { "parameter" => [$group] ] ); + + if (!OcisHelper::isTestingOnReva()) { + array_push( + $substitutions, + [ + "code" => "%shares_drive_id%", + "function" => [ + $this->spacesContext, + "getSpaceIdByName" + ], + "parameter" => [$user, "Shares"] + ] + ); + } } if (!empty($additionalSubstitutions)) { diff --git a/tests/acceptance/features/bootstrap/SettingsContext.php b/tests/acceptance/features/bootstrap/SettingsContext.php index 90d31ef4f..c099a131b 100644 --- a/tests/acceptance/features/bootstrap/SettingsContext.php +++ b/tests/acceptance/features/bootstrap/SettingsContext.php @@ -523,6 +523,7 @@ class SettingsContext implements Context { /** * @Given user :user has disabled auto-accepting + * @Given user :user has disabled the auto-sync share * * @param string $user * diff --git a/tests/acceptance/features/bootstrap/SharingNgContext.php b/tests/acceptance/features/bootstrap/SharingNgContext.php index 69cbec4a7..4c6453ea1 100644 --- a/tests/acceptance/features/bootstrap/SharingNgContext.php +++ b/tests/acceptance/features/bootstrap/SharingNgContext.php @@ -65,15 +65,10 @@ class SharingNgContext implements Context { public function createLinkShare(string $user, TableNode $body): ResponseInterface { $bodyRows = $body->getRowsHash(); $space = $bodyRows['space']; - $resourceType = $bodyRows['resourceType']; $resource = $bodyRows['resource']; $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; - if ($resourceType === 'folder') { - $itemId = $this->spacesContext->getResourceId($user, $space, $resource); - } else { - $itemId = $this->spacesContext->getFileId($user, $space, $resource); - } + $itemId = $this->spacesContext->getResourceId($user, $space, $resource); $bodyRows['displayName'] = $bodyRows['displayName'] ?? null; $bodyRows['expirationDateTime'] = $bodyRows['expirationDateTime'] ?? null; @@ -231,6 +226,61 @@ class SharingNgContext implements Context { ); } + /** + * @When user :user updates the last share with the following using the Graph API: + * + * @param string $user + * @param TableNode $table + * + * @return void + */ + public function userUpdatesTheLastShareWithFollowingUsingGraphApi($user, TableNode $table) { + $response = $this->featureContext->shareNgGetLastCreatedUserGroupShare(); + $permissionID = json_decode($response->getBody()->getContents())->value[0]->id; + $this->featureContext->setResponse( + $this->updateResourceShare( + $user, + $table, + $permissionID + ) + ); + } + + /** + * @param string $user + * @param TableNode $body + * @param string $permissionID + * + * @return ResponseInterface + */ + public function updateResourceShare(string $user, TableNode $body, string $permissionID): ResponseInterface { + $bodyRows = $body->getRowsHash(); + $space = $bodyRows['space']; + $resource = $bodyRows['resource']; + $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; + $itemId = $this->spacesContext->getResourceId($user, $space, $resource); + $body = []; + + if (\array_key_exists('permissionsRole', $bodyRows)) { + $body["roles"] = [GraphHelper::getPermissionsRoleIdByName($bodyRows['permissionsRole'])]; + } + + if (\array_key_exists('expirationDateTime', $bodyRows)) { + $body["expirationDateTime"] = $bodyRows['expirationDateTime']; + } + + return GraphHelper::updateShare( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $user, + $this->featureContext->getPasswordForUser($user), + $spaceId, + $itemId, + \json_encode($body), + $permissionID + ); + } + /** * @When user :user sends the following share invitation with file-id :fileId using the Graph API: * @@ -287,39 +337,39 @@ class SharingNgContext implements Context { * @throws Exception */ public function userUpdatesLastPublicLinkShareUsingTheGraphApiWith(string $user, TableNode $body):void { + $this->featureContext->setResponse( + $this->updateLinkShare( + $user, + $body, + $this->featureContext->shareNgGetLastCreatedLinkShareID() + ) + ); + } + + /** + * @param string $user + * @param TableNode $body + * @param string $permissionID + * + * @return ResponseInterface + */ + public function updateLinkShare(string $user, TableNode $body, string $permissionID): ResponseInterface { $bodyRows = $body->getRowsHash(); $space = $bodyRows['space']; - $resourceType = $bodyRows['resourceType']; $resource = $bodyRows['resource']; $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; - if ($resourceType === 'folder') { - $itemId = $this->spacesContext->getResourceId($user, $space, $resource); - } else { - $itemId = $this->spacesContext->getFileId($user, $space, $resource); + $itemId = $this->spacesContext->getResourceId($user, $space, $resource); + $body = []; + + if (\array_key_exists('permissionsRole', $bodyRows)) { + $body["link"]["type"] = $bodyRows['permissionsRole']; } - if (\array_key_exists('role', $bodyRows) && \array_key_exists('expirationDateTime', $bodyRows)) { - $body = [ - "expirationDateTime" => $bodyRows['expirationDateTime'], - "link" => [ - "type" => $bodyRows['permissionsRole'] - ] - ]; - } elseif (\array_key_exists('permissionsRole', $bodyRows)) { - $body = [ - "link" => [ - "type" => $bodyRows['permissionsRole'] - ] - ]; - } elseif (\array_key_exists('expirationDateTime', $bodyRows)) { - $body = [ - "expirationDateTime" => $bodyRows['expirationDateTime'] - ]; - } else { - throw new Error('Expiration date or role is missing to update for share link!'); + if (\array_key_exists('expirationDateTime', $bodyRows)) { + $body["expirationDateTime"] = $bodyRows['expirationDateTime']; } - $response = GraphHelper::updateLinkShare( + return GraphHelper::updateShare( $this->featureContext->getBaseUrl(), $this->featureContext->getStepLineRef(), $user, @@ -327,9 +377,8 @@ class SharingNgContext implements Context { $spaceId, $itemId, \json_encode($body), - $this->featureContext->shareNgGetLastCreatedLinkShareID() + $permissionID ); - $this->featureContext->setResponse($response); } /** @@ -345,14 +394,9 @@ class SharingNgContext implements Context { public function userSetsOrUpdatesFollowingPasswordForLastLinkShareUsingTheGraphApi(string $user, TableNode $body):void { $bodyRows = $body->getRowsHash(); $space = $bodyRows['space']; - $resourceType = $bodyRows['resourceType']; $resource = $bodyRows['resource']; $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; - if ($resourceType === 'folder') { - $itemId = $this->spacesContext->getResourceId($user, $space, $resource); - } else { - $itemId = $this->spacesContext->getFileId($user, $space, $resource); - } + $itemId = $this->spacesContext->getResourceId($user, $space, $resource); if (\array_key_exists('password', $bodyRows)) { $body = [ @@ -378,7 +422,6 @@ class SharingNgContext implements Context { /** * @param string $sharer * @param string $shareType (user|group) - * @param string $resourceType * @param string $resource * @param string $space * @param string|null $sharee can be both user or group @@ -390,15 +433,12 @@ class SharingNgContext implements Context { public function removeSharePermission( string $sharer, string $shareType, - string $resourceType, string $resource, string $space, ?string $sharee = null ): ResponseInterface { $spaceId = ($this->spacesContext->getSpaceByName($sharer, $space))["id"]; - $itemId = ($resourceType === 'folder') - ? $this->spacesContext->getResourceId($sharer, $space, $resource) - : $this->spacesContext->getFileId($sharer, $space, $resource); + $itemId = $this->spacesContext->getResourceId($sharer, $space, $resource); $permId = ($shareType === 'link') ? $this->featureContext->shareNgGetLastCreatedLinkShareID() @@ -416,12 +456,11 @@ class SharingNgContext implements Context { } /** - * @When /^user "([^"]*)" removes the share permission of (user|group) "([^"]*)" from (file|folder) "([^"]*)" of space "([^"]*)" using the Graph API$/ + * @When /^user "([^"]*)" removes the share permission of (user|group) "([^"]*)" from (?:file|folder|resource) "([^"]*)" of space "([^"]*)" using the Graph API$/ * * @param string $sharer * @param string $shareType (user|group) * @param string $sharee can be both user or group - * @param string $resourceType * @param string $resource * @param string $space * @@ -433,20 +472,18 @@ class SharingNgContext implements Context { string $sharer, string $shareType, string $sharee, - string $resourceType, string $resource, string $space ): void { $this->featureContext->setResponse( - $this->removeSharePermission($sharer, $shareType, $resourceType, $resource, $space) + $this->removeSharePermission($sharer, $shareType, $resource, $space) ); } /** - * @When /^user "([^"]*)" removes the share permission of link from (file|folder) "([^"]*)" of space "([^"]*)" using the Graph API$/ + * @When /^user "([^"]*)" removes the share permission of link from (?:file|folder) "([^"]*)" of space "([^"]*)" using the Graph API$/ * * @param string $sharer - * @param string $resourceType * @param string $resource * @param string $space * @@ -456,12 +493,11 @@ class SharingNgContext implements Context { */ public function userRemovesSharePermissionOfAResourceInLinkShareUsingGraphAPI( string $sharer, - string $resourceType, string $resource, string $space ):void { $this->featureContext->setResponse( - $this->removeSharePermission($sharer, 'link', $resourceType, $resource, $space) + $this->removeSharePermission($sharer, 'link', $resource, $space) ); } diff --git a/tests/acceptance/features/bootstrap/SpacesContext.php b/tests/acceptance/features/bootstrap/SpacesContext.php index de0020bb5..ea1197869 100644 --- a/tests/acceptance/features/bootstrap/SpacesContext.php +++ b/tests/acceptance/features/bootstrap/SpacesContext.php @@ -347,7 +347,7 @@ class SpacesContext implements Context { if ($folderName === $space["name"]) { $folderName = ''; } - $fullUrl = $this->baseUrl . $this->davSpacesUrl . $space["id"] . "/" . $folderName; + $fullUrl = $space["root"]["webDavUrl"] . "/" . $folderName; $response = HttpRequestHelper::sendRequest( $fullUrl, $this->featureContext->getStepLineRef(), diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php index 65c07b7e7..03e15d2d3 100644 --- a/tests/acceptance/features/bootstrap/WebDav.php +++ b/tests/acceptance/features/bootstrap/WebDav.php @@ -5280,6 +5280,7 @@ trait WebDav { $should = ($shouldOrNot !== "not"); foreach ($elementRows as $expectedFile) { $resource = $expectedFile[0]; + $resource = $this->substituteInLineCodes($resource, $user); if ($resource === '') { continue; } @@ -5769,7 +5770,12 @@ trait WebDav { $href = (string)$item->xpath("d:href")[0]; $shareRootXml = $item->xpath("d:propstat//oc:shareroot"); $href = \str_replace($spacesBaseUrl, "", $href); - $resourcePath = \substr($href, \strpos($href, '/') + 1); + $resourcePath = $href; + // do not try to parse the resouce path + // if the item to search is space itself + if (!GraphHelper::isSpaceId($entryNameToSearch ?? '')) { + $resourcePath = \substr($href, \strpos($href, '/') + 1); + } if (\count($shareRootXml)) { $shareroot = \trim((string)$shareRootXml[0], "/"); $resourcePath = $shareroot . "/" . $resourcePath; diff --git a/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature b/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature index 9c59063c4..8ee20c53b 100644 --- a/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature +++ b/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature @@ -9,30 +9,131 @@ Feature: sharing | Brian | | Carol | + @issue-8242 + Scenario Outline: sharer renames the shared item (old/new webdav) + Given user "Alice" has uploaded file with content "foo" to "sharefile.txt" + And using DAV path + And user "Alice" has shared file "sharefile.txt" with user "Brian" + And user "Alice" has shared file "sharefile.txt" with user "Carol" + When user "Alice" moves file "sharefile.txt" to "renamedsharefile.txt" using the WebDAV API + Then the HTTP status code should be "201" + And as "Alice" file "renamedsharefile.txt" should exist + And as "Brian" file "Shares/sharefile.txt" should exist + And as "Carol" file "Shares/sharefile.txt" should exist + When user "Alice" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Alice" the value of the item "//oc:name" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + And as user "Alice" the value of the item "//d:displayname" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + When user "Brian" sends HTTP method "PROPFIND" to URL "/Shares" + Then the HTTP status code should be "207" + And as user "Brian" the value of the item "//oc:name" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + And as user "Brian" the value of the item "//d:displayname" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + When user "Carol" sends HTTP method "PROPFIND" to URL "/Shares" + Then the HTTP status code should be "207" + And as user "Carol" the value of the item "//oc:name" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + And as user "Carol" the value of the item "//d:displayname" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + Examples: + | dav-path-version | dav-path | + | old | /remote.php/webdav | + | new | /remote.php/dav/files/%username% | + | new | /dav/files/%username% | - Scenario: keep user/group shares when the user renames the share within the Shares folder + @issue-8242 + Scenario Outline: sharer renames the shared item (spaces webdav) + Given user "Alice" has uploaded file with content "foo" to "sharefile.txt" + And user "Alice" has shared file "sharefile.txt" with user "Brian" + And user "Alice" has shared file "sharefile.txt" with user "Carol" + When user "Alice" moves file "sharefile.txt" to "renamedsharefile.txt" using the WebDAV API + Then the HTTP status code should be "201" + And as "Alice" file "renamedsharefile.txt" should exist + And as "Brian" file "Shares/sharefile.txt" should exist + And as "Carol" file "Shares/sharefile.txt" should exist + And using spaces DAV path + When user "Alice" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Alice" the value of the item "//oc:name" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + And as user "Alice" the value of the item "//d:displayname" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + When user "Brian" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Brian" the value of the item "//oc:name" of path "/sharefile.txt" in the response should be "sharefile.txt" + And as user "Brian" the value of the item "//d:displayname" of path "/sharefile.txt" in the response should be "sharefile.txt" + When user "Carol" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Carol" the value of the item "//oc:name" of path "/sharefile.txt" in the response should be "sharefile.txt" + And as user "Carol" the value of the item "//d:displayname" of path "/sharefile.txt" in the response should be "sharefile.txt" + Examples: + | dav-path | dav-path-personal | + | /remote.php/dav/spaces/%shares_drive_id% | /remote.php/dav/spaces/%spaceid% | + | /dav/spaces/%shares_drive_id% | /remote.php/dav/spaces/%spaceid% | + + @issue-8242 + Scenario Outline: share receiver renames the shared item (old/new webdav) + Given user "Alice" has uploaded file with content "foo" to "/sharefile.txt" + And using DAV path + And user "Alice" has shared file "sharefile.txt" with user "Brian" + And user "Alice" has shared file "sharefile.txt" with user "Carol" + When user "Carol" moves file "Shares/sharefile.txt" to "Shares/renamedsharefile.txt" using the WebDAV API + Then the HTTP status code should be "201" + And as "Carol" file "Shares/renamedsharefile.txt" should exist + And as "Brian" file "Shares/sharefile.txt" should exist + And as "Alice" file "sharefile.txt" should exist + When user "Carol" sends HTTP method "PROPFIND" to URL "/Shares" + Then the HTTP status code should be "207" + And as user "Carol" the value of the item "//oc:name" of path "/Shares/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + And as user "Carol" the value of the item "//d:displayname" of path "/Shares/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + When user "Alice" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Alice" the value of the item "//oc:name" of path "/sharefile.txt" in the response should be "sharefile.txt" + And as user "Alice" the value of the item "//d:displayname" of path "/sharefile.txt" in the response should be "sharefile.txt" + When user "Brian" sends HTTP method "PROPFIND" to URL "/Shares" + Then the HTTP status code should be "207" + And as user "Brian" the value of the item "//oc:name" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + And as user "Brian" the value of the item "//d:displayname" of path "/Shares/sharefile.txt" in the response should be "sharefile.txt" + Examples: + | dav-path-version | dav-path | + | old | /remote.php/webdav | + | new | /remote.php/dav/files/%username% | + | new | /dav/files/%username% | + + @issue-8242 + Scenario Outline: share receiver renames the shared item (spaces webdav) + Given user "Alice" has uploaded file with content "foo" to "/sharefile.txt" + And user "Alice" has shared file "sharefile.txt" with user "Brian" + And user "Alice" has shared file "sharefile.txt" with user "Carol" + When user "Carol" moves file "Shares/sharefile.txt" to "Shares/renamedsharefile.txt" using the WebDAV API + Then the HTTP status code should be "201" + And as "Carol" file "Shares/renamedsharefile.txt" should exist + And as "Brian" file "Shares/sharefile.txt" should exist + And as "Alice" file "sharefile.txt" should exist + And using spaces DAV path + When user "Carol" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Carol" the value of the item "//oc:name" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + And as user "Carol" the value of the item "//d:displayname" of path "/renamedsharefile.txt" in the response should be "renamedsharefile.txt" + When user "Alice" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Alice" the value of the item "//oc:name" of path "/sharefile.txt" in the response should be "sharefile.txt" + And as user "Alice" the value of the item "//d:displayname" of path "/sharefile.txt" in the response should be "sharefile.txt" + When user "Brian" sends HTTP method "PROPFIND" to URL "" + Then the HTTP status code should be "207" + And as user "Brian" the value of the item "//oc:name" of path "/sharefile.txt" in the response should be "sharefile.txt" + And as user "Brian" the value of the item "//d:displayname" of path "/sharefile.txt" in the response should be "sharefile.txt" + Examples: + | dav-path | dav-path-personal | + | /remote.php/dav/spaces/%shares_drive_id% | /remote.php/dav/spaces/%spaceid% | + | /dav/spaces/%shares_drive_id% | /remote.php/dav/spaces/%spaceid% | + + + Scenario: keep group share when the one user renames the share and the user is deleted Given group "grp1" has been created And user "Brian" has been added to group "grp1" And user "Carol" has been added to group "grp1" And user "Alice" has created folder "/TMP" - When user "Alice" shares folder "TMP" with group "grp1" using the sharing API - And user "Brian" moves folder "/Shares/TMP" to "/Shares/new" using the WebDAV API + And user "Alice" has shared folder "TMP" with group "grp1" + When user "Carol" moves folder "/Shares/TMP" to "/Shares/new" using the WebDAV API And the administrator deletes user "Carol" using the provisioning API - Then the OCS status code of responses on all endpoints should be "100" - And the HTTP status code of responses on each endpoint should be "200, 201, 204" respectively - And user "Brian" should see the following elements - | /Shares/new/| - - - Scenario: keep user shared file name same after one of recipient has renamed the file inside the Shares folder - Given user "Alice" has uploaded file with content "foo" to "/sharefile.txt" - And user "Alice" has shared file "/sharefile.txt" with user "Brian" - And user "Alice" has shared file "/sharefile.txt" with user "Carol" - When user "Carol" moves file "/Shares/sharefile.txt" to "/Shares/renamedsharefile.txt" using the WebDAV API - Then the HTTP status code should be "201" - And as "Carol" file "/Shares/renamedsharefile.txt" should exist - And as "Alice" file "/sharefile.txt" should exist - And as "Brian" file "/Shares/sharefile.txt" should exist + Then the HTTP status code of responses on each endpoint should be "201, 204" respectively + And as "Alice" file "Shares/TMP" should not exist Scenario: receiver renames a received share with share, read, change permissions inside the Shares folder diff --git a/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature b/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature index 518ac93df..0e4a398e5 100644 --- a/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature +++ b/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature @@ -8,22 +8,7 @@ Feature: upload to a public link share Background: Given user "Alice" has been created with default attributes and without skeleton files And user "Alice" has created folder "FOLDER" - - @smokeTest @issue-1267 - Scenario: uploading same file to a public upload-only share multiple times via new API - # The new API does the auto rename in upload-only folders - Given user "Alice" has created a public link share with settings - | path | FOLDER | - | permissions | create | - | password | %public% | - When the public uploads file "test.txt" with password "%public%" and content "test" using the new public WebDAV API - When the public uploads file "test.txt" with password "%public%" and content "test2" using the new public WebDAV API - Then the HTTP status code of responses on all endpoints should be "201" - And the following headers should match these regular expressions - | ETag | /^"[a-f0-9:\.]{1,32}"$/ | - And the content of file "/FOLDER/test.txt" for user "Alice" should be "test" - And the content of file "/FOLDER/test (2).txt" for user "Alice" should be "test2" - + Scenario Outline: uploading file to a public upload-only share using public API that was deleted does not work Given using DAV path diff --git a/tests/acceptance/features/coreApiVersions/fileVersions.feature b/tests/acceptance/features/coreApiVersions/fileVersions.feature index 9658d6f61..56e1180aa 100644 --- a/tests/acceptance/features/coreApiVersions/fileVersions.feature +++ b/tests/acceptance/features/coreApiVersions/fileVersions.feature @@ -163,13 +163,13 @@ Feature: dav-versions Then the HTTP status code should be "200" And the following headers should be set | header | value | - | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" | + | Content-Disposition | attachment; filename*=UTF-8''"textfile0.txt"; filename="textfile0.txt" | And the downloaded content should be "version 1" When user "Alice" downloads the version of file "textfile0.txt" with the index "2" Then the HTTP status code should be "200" And the following headers should be set | header | value | - | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" | + | Content-Disposition | attachment; filename*=UTF-8''"textfile0.txt"; filename="textfile0.txt" | And the downloaded content should be "uploaded content" @skipOnStorage:ceph @skipOnStorage:scality @@ -182,13 +182,13 @@ Feature: dav-versions Then the HTTP status code should be "200" And the following headers should be set | header | value | - | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" | + | Content-Disposition | attachment; filename*=UTF-8''"textfile0.txt"; filename="textfile0.txt" | And the downloaded content should be "version 2" When user "Alice" downloads the version of file "textfile0.txt" with the index "2" Then the HTTP status code should be "200" And the following headers should be set | header | value | - | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" | + | Content-Disposition | attachment; filename*=UTF-8''"textfile0.txt"; filename="textfile0.txt" | And the downloaded content should be "uploaded content" diff --git a/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature b/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature index 34b7be65f..a028ceada 100644 --- a/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature +++ b/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature @@ -262,22 +262,56 @@ Feature: download file | dav-path-version | | spaces | - @smokeTest + @smokeTest @issue-8361 Scenario Outline: downloading a file should serve security headers Given using DAV path - When user "Alice" downloads file "/welcome.txt" using the WebDAV API + And user "Alice" has uploaded file with content "test file" to "/" + When user "Alice" downloads file "/" using the WebDAV API Then the HTTP status code should be "200" And the following headers should be set - | header | value | - | Content-Disposition | attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt" | - | Content-Security-Policy | default-src 'none'; | - | X-Content-Type-Options | nosniff | - | X-Download-Options | noopen | - | X-Frame-Options | SAMEORIGIN | - | X-Permitted-Cross-Domain-Policies | none | - | X-Robots-Tag | none | - | X-XSS-Protection | 1; mode=block | - And the downloaded content should start with "Welcome" + | header | value | + | Content-Disposition | attachment; filename*=UTF-8''""; filename="" | + | Content-Security-Policy | default-src 'none'; | + | X-Content-Type-Options | nosniff | + | X-Download-Options | noopen | + | X-Frame-Options | SAMEORIGIN | + | X-Permitted-Cross-Domain-Policies | none | + | X-Robots-Tag | none | + | X-XSS-Protection | 1; mode=block | + And the downloaded content should be "test file" + Examples: + | dav-path-version | file | + | old | textfile.txt | + | old | comma,.txt | + | old | 'quote'single'.txt | + | new | textfile.txt | + | new | comma,.txt | + | new | 'quote'single'.txt | + + @skipOnRevaMaster + Examples: + | dav-path-version | file | + | spaces | textfile.txt | + | spaces | comma,.txt | + | spaces | 'quote'single'.txt | + + @smokeTest @issue-8361 + Scenario Outline: downloading a file should serve security headers (file with doubel quotes) + Given using DAV path + And user "Alice" has uploaded file with content "test file" to '/"quote"double".txt' + When user "Alice" downloads file '/"quote"double".txt' using the WebDAV API + Then the HTTP status code should be "200" + And the following headers should be set + | header | value | + | Content-Disposition | attachment; filename*=UTF-8''""quote"double".txt"; filename=""quote"double".txt" | + | Content-Security-Policy | default-src 'none'; | + | X-Content-Type-Options | nosniff | + | X-Download-Options | noopen | + | X-Frame-Options | SAMEORIGIN | + | X-Permitted-Cross-Domain-Policies | none | + | X-Robots-Tag | none | + | X-XSS-Protection | 1; mode=block | + And the downloaded content should be "test file" Examples: | dav-path-version | | old | diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/context.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/context.go new file mode 100644 index 000000000..7c0a6d443 --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/context.go @@ -0,0 +1,20 @@ +package ocdav + +import ( + "context" + + cs3storage "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" +) + +type tokenStatInfoKey struct{} + +// ContextWithTokenStatInfo adds the token stat info to the context +func ContextWithTokenStatInfo(ctx context.Context, info *cs3storage.ResourceInfo) context.Context { + return context.WithValue(ctx, tokenStatInfoKey{}, info) +} + +// TokenStatInfoFromContext returns the token stat info from the context +func TokenStatInfoFromContext(ctx context.Context) (*cs3storage.ResourceInfo, bool) { + v, ok := ctx.Value(tokenStatInfoKey{}).(*cs3storage.ResourceInfo) + return v, ok +} diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go index 93e12da1d..db26d729b 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/dav.go @@ -44,8 +44,6 @@ const ( _trashbinPath = "trash-bin" ) -type tokenStatInfoKey struct{} - // DavHandler routes to the different sub handlers type DavHandler struct { AvatarsHandler *AvatarsHandler @@ -318,9 +316,9 @@ func (h *DavHandler) Handler(s *svc) http.Handler { } log.Debug().Interface("statInfo", sRes.Info).Msg("Stat info from public link token path") + ctx := ContextWithTokenStatInfo(ctx, sRes.Info) + r = r.WithContext(ctx) if sRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_CONTAINER { - ctx := context.WithValue(ctx, tokenStatInfoKey{}, sRes.Info) - r = r.WithContext(ctx) h.PublicFileHandler.Handler(s).ServeHTTP(w, r) } else { h.PublicFolderHandler.Handler(s).ServeHTTP(w, r) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors/error.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors/error.go index b573dd20a..78df1d198 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors/error.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors/error.go @@ -159,6 +159,8 @@ var ( ErrNoSuchLock = errors.New("webdav: no such lock") // ErrNotImplemented is returned when hitting not implemented code paths ErrNotImplemented = errors.New("webdav: not implemented") + // ErrTokenNotFound is returned when a token is not found + ErrTokenStatInfoMissing = errors.New("webdav: token stat info missing") ) // HandleErrorStatus checks the status code, logs a Debug or Error level message diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/filedrop.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/filedrop.go new file mode 100644 index 000000000..53cb41799 --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/filedrop.go @@ -0,0 +1,47 @@ +package ocdav + +import ( + "context" + "errors" + "path/filepath" + "strconv" + "strings" + + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" +) + +// FindName returns the next filename available when the current +func FindName(ctx context.Context, client gatewayv1beta1.GatewayAPIClient, name string, parentid *provider.ResourceId) (string, *rpc.Status, error) { + lReq := &provider.ListContainerRequest{ + Ref: &provider.Reference{ + ResourceId: parentid, + }, + } + lRes, err := client.ListContainer(ctx, lReq) + if err != nil { + return "", nil, err + } + if lRes.Status.Code != rpc.Code_CODE_OK { + return "", lRes.Status, nil + } + // iterate over the listing to determine next suffix + var itemMap = make(map[string]struct{}) + for _, fi := range lRes.Infos { + itemMap[fi.GetName()] = struct{}{} + } + ext := filepath.Ext(name) + fileName := strings.TrimSuffix(name, ext) + if strings.HasSuffix(fileName, ".tar") { + fileName = strings.TrimSuffix(fileName, ".tar") + ext = filepath.Ext(fileName) + "." + ext + } + // starts with two because "normal" humans begin counting with 1 and we say the existing file is the first one + for i := 2; i < len(itemMap)+3; i++ { + if _, ok := itemMap[fileName+" ("+strconv.Itoa(i)+")"+ext]; !ok { + return fileName + " (" + strconv.Itoa(i) + ")" + ext, lRes.GetStatus(), nil + } + } + return "", nil, errors.New("could not determine new filename") +} diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go index fbd51ee72..4706d20e9 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/move.go @@ -266,7 +266,11 @@ func (s *svc) handleMove(ctx context.Context, w http.ResponseWriter, r *http.Req } // TODO what if intermediate is a file? } - + // resolve the destination path + if dst.Path == "." { + dst.Path = utils.MakeRelativePath(dstStatRes.GetInfo().GetName()) + dst.ResourceId = dstStatRes.GetInfo().GetParentId() + } mReq := &provider.MoveRequest{Source: src, Destination: dst} mRes, err := client.Move(ctx, mReq) if err != nil { diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net/builders.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net/builders.go index 0498072b6..be7b8fdee 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net/builders.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net/builders.go @@ -27,7 +27,7 @@ import ( // ContentDispositionAttachment builds a ContentDisposition Attachment header with various filename encodings func ContentDispositionAttachment(filename string) string { - return "attachment; filename*=UTF-8''" + filename + "; filename=\"" + filename + "\"" + return "attachment; filename*=UTF-8''\"" + filename + "\"; filename=\"" + filename + "\"" } // RFC1123Z formats a CS3 Timestamp to be used in HTTP headers like Last-Modified diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go index fc5764738..027f8c73b 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -579,6 +579,7 @@ func (p *Handler) getResourceInfos(ctx context.Context, w http.ResponseWriter, r // adjust path info.Path = filepath.Join(spacePath, spaceRef.Path) + info.Name = filepath.Base(info.Path) spaceMap[info] = spaceData{Ref: spaceRef, SpaceType: space.SpaceType} diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/publicfile.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/publicfile.go index 31c92616b..91bfe5f1b 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/publicfile.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/publicfile.go @@ -26,7 +26,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" + ocdaverrors "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" "github.com/cs3org/reva/v2/pkg/appctx" @@ -96,7 +96,16 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(r.Context(), "token_propfind") defer span.End() - tokenStatInfo := ctx.Value(tokenStatInfoKey{}).(*provider.ResourceInfo) + tokenStatInfo, ok := TokenStatInfoFromContext(ctx) + if !ok { + span.RecordError(ocdaverrors.ErrTokenStatInfoMissing) + span.SetStatus(codes.Error, ocdaverrors.ErrTokenStatInfoMissing.Error()) + span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusInternalServerError)) + w.WriteHeader(http.StatusInternalServerError) + b, err := ocdaverrors.Marshal(http.StatusInternalServerError, ocdaverrors.ErrTokenStatInfoMissing.Error(), "") + ocdaverrors.HandleWebdavError(appctx.GetLogger(ctx), w, b, err) + return + } sublog := appctx.GetLogger(ctx).With().Interface("tokenStatInfo", tokenStatInfo).Logger() sublog.Debug().Msg("handlePropfindOnToken") @@ -109,20 +118,20 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s sublog.Debug().Str("depth", dh).Msg(err.Error()) w.WriteHeader(http.StatusBadRequest) m := fmt.Sprintf("Invalid Depth header value: %v", dh) - b, err := errors.Marshal(http.StatusBadRequest, m, "") - errors.HandleWebdavError(&sublog, w, b, err) + b, err := ocdaverrors.Marshal(http.StatusBadRequest, m, "") + ocdaverrors.HandleWebdavError(&sublog, w, b, err) return } if depth == net.DepthInfinity && !s.c.AllowPropfindDepthInfinitiy { - span.RecordError(errors.ErrInvalidDepth) + span.RecordError(ocdaverrors.ErrInvalidDepth) span.SetStatus(codes.Error, "DEPTH: infinity is not supported") span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest)) - sublog.Debug().Str("depth", dh).Msg(errors.ErrInvalidDepth.Error()) + sublog.Debug().Str("depth", dh).Msg(ocdaverrors.ErrInvalidDepth.Error()) w.WriteHeader(http.StatusBadRequest) m := fmt.Sprintf("Invalid Depth header value: %v", dh) - b, err := errors.Marshal(http.StatusBadRequest, m, "") - errors.HandleWebdavError(&sublog, w, b, err) + b, err := ocdaverrors.Marshal(http.StatusBadRequest, m, "") + ocdaverrors.HandleWebdavError(&sublog, w, b, err) return } diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go index 485629a3f..5add5740d 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/put.go @@ -155,6 +155,47 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ w.WriteHeader(http.StatusInternalServerError) return } + + // Test if the target is a secret filedrop + tokenStatInfo, ok := TokenStatInfoFromContext(ctx) + // We assume that when the uploader can create containers, but is not allowed to list them, it is a secret file drop + if ok && tokenStatInfo.GetPermissionSet().CreateContainer && !tokenStatInfo.GetPermissionSet().ListContainer { + // TODO we can skip this stat if the tokenStatInfo is the direct parent + sReq := &provider.StatRequest{ + Ref: ref, + } + sRes, err := client.Stat(ctx, sReq) + if err != nil { + log.Error().Err(err).Msg("error sending grpc stat request") + w.WriteHeader(http.StatusInternalServerError) + return + } + + // We also need to continue if we are not allowed to stat a resource. We may not have stat permission. That still means it exists and we need to find a new filename. + switch sRes.Status.Code { + case rpc.Code_CODE_OK, rpc.Code_CODE_PERMISSION_DENIED: + // find next filename + newName, status, err := FindName(ctx, client, filepath.Base(ref.Path), sRes.GetInfo().GetParentId()) + if err != nil { + log.Error().Err(err).Msg("error sending grpc stat request") + w.WriteHeader(http.StatusInternalServerError) + return + } + if status.Code != rpc.Code_CODE_OK { + log.Error().Interface("status", status).Msg("error listing file") + errors.HandleErrorStatus(&log, w, status) + return + } + ref.Path = utils.MakeRelativePath(filepath.Join(filepath.Dir(ref.GetPath()), newName)) + case rpc.Code_CODE_NOT_FOUND: + // just continue with normal upload + default: + log.Error().Interface("status", sRes.Status).Msg("error stating file") + errors.HandleErrorStatus(&log, w, sRes.Status) + return + } + } + opaque := &typespb.Opaque{} if mtime := r.Header.Get(net.HeaderOCMtime); mtime != "" { utils.AppendPlainToOpaque(opaque, net.HeaderOCMtime, mtime) diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go index c32f4022a..bd5831d4e 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/tus.go @@ -24,6 +24,7 @@ import ( "io" "net/http" "path" + "path/filepath" "strconv" "strings" "time" @@ -115,6 +116,15 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. w.WriteHeader(http.StatusPreconditionFailed) return } + + // Test if the target is a secret filedrop + var isSecretFileDrop bool + tokenStatInfo, ok := TokenStatInfoFromContext(ctx) + // We assume that when the uploader can create containers, but is not allowed to list them, it is a secret file drop + if ok && tokenStatInfo.GetPermissionSet().CreateContainer && !tokenStatInfo.GetPermissionSet().ListContainer { + isSecretFileDrop = true + } + // r.Header.Get(net.HeaderOCChecksum) // TODO must be SHA1, ADLER32 or MD5 ... in capital letters???? // curl -X PUT https://demo.owncloud.com/remote.php/webdav/testcs.bin -u demo:demo -d '123' -v -H 'OC-Checksum: SHA1:40bd001563085fc35165329ea1ff5c5ecbdbbeef' @@ -158,6 +168,22 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http. return } } + if isSecretFileDrop { + // find next filename + newName, status, err := FindName(ctx, client, filepath.Base(ref.Path), sRes.GetInfo().GetParentId()) + if err != nil { + log.Error().Err(err).Msg("error sending grpc stat request") + w.WriteHeader(http.StatusInternalServerError) + return + } + if status.GetCode() != rpc.Code_CODE_OK { + log.Error().Interface("status", status).Msg("error listing file") + errors.HandleErrorStatus(&log, w, status) + return + } + ref.Path = filepath.Join(filepath.Dir(ref.GetPath()), newName) + sRes.GetInfo().Name = newName + } } uploadLength, err := strconv.ParseInt(r.Header.Get(net.HeaderUploadLength), 10, 64) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/events/sse.go b/vendor/github.com/cs3org/reva/v2/pkg/events/sse.go index 34841c81f..fa3459d9f 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/events/sse.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/events/sse.go @@ -4,9 +4,9 @@ import ( "encoding/json" ) -// SendSEE instructs the sse service to send a notification to a user +// SendSSE instructs the sse service to send one or multiple notifications type SendSSE struct { - UserID string + UserIDs []string Type string Message []byte } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/json.go b/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/json.go index 7e809f552..1d2b44265 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/json.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/json.go @@ -72,10 +72,6 @@ func NewFile(c map[string]interface{}) (publicshare.Manager, error) { } p := file.New(conf.File) - if err := p.Init(context.Background()); err != nil { - return nil, err - } - return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p) } @@ -89,10 +85,6 @@ func NewMemory(c map[string]interface{}) (publicshare.Manager, error) { conf.init() p := memory.New() - if err := p.Init(context.Background()); err != nil { - return nil, err - } - return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p) } @@ -111,10 +103,6 @@ func NewCS3(c map[string]interface{}) (publicshare.Manager, error) { } p := cs3.New(s) - if err := p.Init(context.Background()); err != nil { - return nil, err - } - return New(conf.GatewayAddr, conf.SharePasswordHashCost, conf.JanitorRunInterval, conf.EnableExpiredSharesCleanup, p) } @@ -174,6 +162,10 @@ type manager struct { enableExpiredSharesCleanup bool } +func (m *manager) init() error { + return m.persistence.Init(context.Background()) +} + func (m *manager) startJanitorRun() { if !m.enableExpiredSharesCleanup { return @@ -200,6 +192,10 @@ func (m *manager) Dump(ctx context.Context, shareChan chan<- *publicshare.WithPa m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return err + } + db, err := m.persistence.Read(ctx) if err != nil { return err @@ -222,6 +218,10 @@ func (m *manager) Load(ctx context.Context, shareChan <-chan *publicshare.WithPa m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return err + } + db, err := m.persistence.Read(ctx) if err != nil { return err @@ -296,6 +296,10 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return nil, err + } + encShare, err := utils.MarshalProtoV1ToJSON(&ps.PublicShare) if err != nil { return nil, err @@ -385,6 +389,10 @@ func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return nil, err + } + db, err := m.persistence.Read(ctx) if err != nil { return nil, err @@ -420,6 +428,10 @@ func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.Pu m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return nil, err + } + if ref.GetToken() != "" { ps, pw, err := m.getByToken(ctx, ref.GetToken()) if err != nil { @@ -473,6 +485,10 @@ func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters [] m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return nil, err + } + log := appctx.GetLogger(ctx) db, err := m.persistence.Read(ctx) @@ -555,6 +571,10 @@ func (m *manager) cleanupExpiredShares() { m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return + } + db, _ := m.persistence.Read(context.Background()) for _, v := range db { @@ -594,6 +614,11 @@ func (m *manager) revokeExpiredPublicShare(ctx context.Context, s *link.PublicSh func (m *manager) RevokePublicShare(ctx context.Context, _ *user.User, ref *link.PublicShareReference) error { m.mutex.Lock() defer m.mutex.Unlock() + + if err := m.init(); err != nil { + return err + } + return m.revokePublicShare(ctx, ref) } @@ -651,6 +676,10 @@ func (m *manager) GetPublicShareByToken(ctx context.Context, token string, auth m.mutex.Lock() defer m.mutex.Unlock() + if err := m.init(); err != nil { + return nil, err + } + db, err := m.persistence.Read(ctx) if err != nil { return nil, err diff --git a/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/file/file.go b/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/file/file.go index d9a84d674..e813b95dc 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/file/file.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence/file/file.go @@ -24,6 +24,7 @@ import ( "fmt" "os" "path/filepath" + "sync" "github.com/cs3org/reva/v2/pkg/publicshare/manager/json/persistence" ) @@ -31,16 +32,28 @@ import ( type file struct { path string initialized bool + lock *sync.RWMutex } // New returns a new Cache instance func New(path string) persistence.Persistence { return &file{ path: path, + lock: &sync.RWMutex{}, } } func (p *file) Init(_ context.Context) error { + if p.isInitialized() { + return nil + } + + p.lock.Lock() + defer p.lock.Unlock() + if p.initialized { + return nil + } + // attempt to create the db file var fi os.FileInfo var err error @@ -65,7 +78,7 @@ func (p *file) Init(_ context.Context) error { } func (p *file) Read(_ context.Context) (persistence.PublicShares, error) { - if !p.initialized { + if !p.isInitialized() { return nil, fmt.Errorf("not initialized") } db := map[string]interface{}{} @@ -80,7 +93,7 @@ func (p *file) Read(_ context.Context) (persistence.PublicShares, error) { } func (p *file) Write(_ context.Context, db persistence.PublicShares) error { - if !p.initialized { + if !p.isInitialized() { return fmt.Errorf("not initialized") } dbAsJSON, err := json.Marshal(db) @@ -90,3 +103,9 @@ func (p *file) Write(_ context.Context, db persistence.PublicShares) error { return os.WriteFile(p.path, dbAsJSON, 0644) } + +func (p *file) isInitialized() bool { + p.lock.RLock() + defer p.lock.RUnlock() + return p.initialized +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/loader/loader.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/loader/loader.go index 68575752c..71475e38a 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/loader/loader.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/loader/loader.go @@ -31,6 +31,7 @@ import ( _ "github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud" _ "github.com/cs3org/reva/v2/pkg/storage/fs/ocis" _ "github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql" + _ "github.com/cs3org/reva/v2/pkg/storage/fs/posix" _ "github.com/cs3org/reva/v2/pkg/storage/fs/s3" _ "github.com/cs3org/reva/v2/pkg/storage/fs/s3ng" // Add your own here diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore/blobstore.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore/blobstore.go new file mode 100644 index 000000000..7afd3eeb5 --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore/blobstore.go @@ -0,0 +1,122 @@ +// Copyright 2018-2021 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 blobstore + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/pkg/errors" +) + +// Blobstore provides an interface to an filesystem based blobstore +type Blobstore struct { + root string +} + +// New returns a new Blobstore +func New(root string) (*Blobstore, error) { + err := os.MkdirAll(root, 0700) + if err != nil { + return nil, err + } + + return &Blobstore{ + root: root, + }, nil +} + +// Upload stores some data in the blobstore under the given key +func (bs *Blobstore) Upload(node *node.Node, source string) error { + dest, err := bs.path(node) + if err != nil { + return err + } + // ensure parent path exists + if err := os.MkdirAll(filepath.Dir(dest), 0700); err != nil { + return errors.Wrap(err, "Decomposedfs: oCIS blobstore: error creating parent folders for blob") + } + + if err := os.Rename(source, dest); err == nil { + return nil + } + + // Rename failed, file needs to be copied. + file, err := os.Open(source) + if err != nil { + return errors.Wrap(err, "Decomposedfs: oCIS blobstore: Can not open source file to upload") + } + defer file.Close() + + f, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0700) + if err != nil { + return errors.Wrapf(err, "could not open blob '%s' for writing", dest) + } + + w := bufio.NewWriter(f) + _, err = w.ReadFrom(file) + if err != nil { + return errors.Wrapf(err, "could not write blob '%s'", dest) + } + + return w.Flush() +} + +// Download retrieves a blob from the blobstore for reading +func (bs *Blobstore) Download(node *node.Node) (io.ReadCloser, error) { + dest, err := bs.path(node) + if err != nil { + return nil, err + } + file, err := os.Open(dest) + if err != nil { + return nil, errors.Wrapf(err, "could not read blob '%s'", dest) + } + return file, nil +} + +// Delete deletes a blob from the blobstore +func (bs *Blobstore) Delete(node *node.Node) error { + dest, err := bs.path(node) + if err != nil { + return err + } + if err := utils.RemoveItem(dest); err != nil { + return errors.Wrapf(err, "could not delete blob '%s'", dest) + } + return nil +} + +func (bs *Blobstore) path(node *node.Node) (string, error) { + if node.BlobID == "" { + return "", fmt.Errorf("blobstore: BlobID is empty") + } + return filepath.Join( + bs.root, + filepath.Clean(filepath.Join( + "/", "spaces", lookup.Pathify(node.SpaceID, 1, 2), "blobs", lookup.Pathify(node.BlobID, 4, 2)), + ), + ), nil +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup/lookup.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup/lookup.go new file mode 100644 index 000000000..d64223f7d --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup/lookup.go @@ -0,0 +1,375 @@ +// Copyright 2018-2021 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 lookup + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/appctx" + "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" + "github.com/cs3org/reva/v2/pkg/storage/utils/templates" + "github.com/pkg/errors" + "github.com/rogpeppe/go-internal/lockedfile" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +var tracer trace.Tracer + +var _spaceTypePersonal = "personal" + +func init() { + tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/lookup") +} + +// Lookup implements transformations from filepath to node and back +type Lookup struct { + Options *options.Options + + metadataBackend metadata.Backend +} + +// New returns a new Lookup instance +func New(b metadata.Backend, o *options.Options) *Lookup { + return &Lookup{ + Options: o, + metadataBackend: b, + } +} + +// MetadataBackend returns the metadata backend +func (lu *Lookup) MetadataBackend() metadata.Backend { + return lu.metadataBackend +} + +// ReadBlobSizeAttr reads the blobsize from the xattrs +func (lu *Lookup) ReadBlobSizeAttr(ctx context.Context, path string) (int64, error) { + blobSize, err := lu.metadataBackend.GetInt64(ctx, path, prefixes.BlobsizeAttr) + if err != nil { + return 0, errors.Wrapf(err, "error reading blobsize xattr") + } + return blobSize, nil +} + +// ReadBlobIDAttr reads the blobsize from the xattrs +func (lu *Lookup) ReadBlobIDAttr(ctx context.Context, path string) (string, error) { + attr, err := lu.metadataBackend.Get(ctx, path, prefixes.BlobIDAttr) + if err != nil { + return "", errors.Wrapf(err, "error reading blobid xattr") + } + return string(attr), nil +} + +// TypeFromPath returns the type of the node at the given path +func (lu *Lookup) TypeFromPath(ctx context.Context, path string) provider.ResourceType { + // Try to read from xattrs + typeAttr, err := lu.metadataBackend.GetInt64(ctx, path, prefixes.TypeAttr) + if err == nil { + return provider.ResourceType(int32(typeAttr)) + } + + t := provider.ResourceType_RESOURCE_TYPE_INVALID + // Fall back to checking on disk + fi, err := os.Lstat(path) + if err != nil { + return t + } + + switch { + case fi.IsDir(): + if _, err = lu.metadataBackend.Get(ctx, path, prefixes.ReferenceAttr); err == nil { + t = provider.ResourceType_RESOURCE_TYPE_REFERENCE + } else { + t = provider.ResourceType_RESOURCE_TYPE_CONTAINER + } + case fi.Mode().IsRegular(): + t = provider.ResourceType_RESOURCE_TYPE_FILE + case fi.Mode()&os.ModeSymlink != 0: + t = provider.ResourceType_RESOURCE_TYPE_SYMLINK + // TODO reference using ext attr on a symlink + // nodeType = provider.ResourceType_RESOURCE_TYPE_REFERENCE + } + return t +} + +// NodeFromResource takes in a request path or request id and converts it to a Node +func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference) (*node.Node, error) { + ctx, span := tracer.Start(ctx, "NodeFromResource") + defer span.End() + + if ref.ResourceId != nil { + // check if a storage space reference is used + // currently, the decomposed fs uses the root node id as the space id + n, err := lu.NodeFromID(ctx, ref.ResourceId) + if err != nil { + return nil, err + } + // is this a relative reference? + if ref.Path != "" { + p := filepath.Clean(ref.Path) + if p != "." && p != "/" { + // walk the relative path + n, err = lu.WalkPath(ctx, n, p, false, func(ctx context.Context, n *node.Node) error { return nil }) + if err != nil { + return nil, err + } + n.SpaceID = ref.ResourceId.SpaceId + } + } + return n, nil + } + + // reference is invalid + return nil, fmt.Errorf("invalid reference %+v. resource_id must be set", ref) +} + +// NodeFromID returns the internal path for the id +func (lu *Lookup) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *node.Node, err error) { + ctx, span := tracer.Start(ctx, "NodeFromID") + defer span.End() + if id == nil { + return nil, fmt.Errorf("invalid resource id %+v", id) + } + if id.OpaqueId == "" { + // The Resource references the root of a space + return lu.NodeFromSpaceID(ctx, id.SpaceId) + } + return node.ReadNode(ctx, lu, id.SpaceId, id.OpaqueId, false, nil, false) +} + +// Pathify segments the beginning of a string into depth segments of width length +// Pathify("aabbccdd", 3, 1) will return "a/a/b/bccdd" +func Pathify(id string, depth, width int) string { + b := strings.Builder{} + i := 0 + for ; i < depth; i++ { + if len(id) <= i*width+width { + break + } + b.WriteString(id[i*width : i*width+width]) + b.WriteRune(filepath.Separator) + } + b.WriteString(id[i*width:]) + return b.String() +} + +// NodeFromSpaceID converts a resource id into a Node +func (lu *Lookup) NodeFromSpaceID(ctx context.Context, spaceID string) (n *node.Node, err error) { + node, err := node.ReadNode(ctx, lu, spaceID, spaceID, false, nil, false) + if err != nil { + return nil, err + } + + node.SpaceRoot = node + return node, nil +} + +// Path returns the path for node +func (lu *Lookup) Path(ctx context.Context, n *node.Node, hasPermission node.PermissionFunc) (p string, err error) { + root := n.SpaceRoot + for n.ID != root.ID { + p = filepath.Join(n.Name, p) + if n, err = n.Parent(ctx); err != nil { + appctx.GetLogger(ctx). + Error().Err(err). + Str("path", p). + Interface("node", n). + Msg("Path()") + return + } + + if !hasPermission(n) { + break + } + } + p = filepath.Join("/", p) + return +} + +// WalkPath calls n.Child(segment) on every path segment in p starting at the node r. +// If a function f is given it will be executed for every segment node, but not the root node r. +// If followReferences is given the current visited reference node is replaced by the referenced node. +func (lu *Lookup) WalkPath(ctx context.Context, r *node.Node, p string, followReferences bool, f func(ctx context.Context, n *node.Node) error) (*node.Node, error) { + segments := strings.Split(strings.Trim(p, "/"), "/") + var err error + for i := range segments { + if r, err = r.Child(ctx, segments[i]); err != nil { + return r, err + } + + if followReferences { + if attrBytes, err := r.Xattr(ctx, prefixes.ReferenceAttr); err == nil { + realNodeID := attrBytes + ref, err := refFromCS3(realNodeID) + if err != nil { + return nil, err + } + + r, err = lu.NodeFromID(ctx, ref.ResourceId) + if err != nil { + return nil, err + } + } + } + if r.IsSpaceRoot(ctx) { + r.SpaceRoot = r + } + + if !r.Exists && i < len(segments)-1 { + return r, errtypes.NotFound(segments[i]) + } + if f != nil { + if err = f(ctx, r); err != nil { + return r, err + } + } + } + return r, nil +} + +// InternalRoot returns the internal storage root directory +func (lu *Lookup) InternalRoot() string { + return lu.Options.Root +} + +// InternalPath returns the internal path for a given ID +func (lu *Lookup) InternalPath(spaceID, nodeID string) string { + return filepath.Join(lu.Options.Root, "spaces", Pathify(spaceID, 1, 2), "nodes", Pathify(nodeID, 4, 2)) +} + +func (lu *Lookup) SpacePath(spaceID string) string { + return filepath.Join(lu.Options.Root, spaceID) +} + +// // ReferenceFromAttr returns a CS3 reference from xattr of a node. +// // Supported formats are: "cs3:storageid/nodeid" +// func ReferenceFromAttr(b []byte) (*provider.Reference, error) { +// return refFromCS3(b) +// } + +// refFromCS3 creates a CS3 reference from a set of bytes. This method should remain private +// and only be called after validation because it can potentially panic. +func refFromCS3(b []byte) (*provider.Reference, error) { + parts := string(b[4:]) + return &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: strings.Split(parts, "/")[0], + OpaqueId: strings.Split(parts, "/")[1], + }, + }, nil +} + +// CopyMetadata copies all extended attributes from source to target. +// The optional filter function can be used to filter by attribute name, e.g. by checking a prefix +// For the source file, a shared lock is acquired. +// NOTE: target resource will be write locked! +func (lu *Lookup) CopyMetadata(ctx context.Context, src, target string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), acquireTargetLock bool) (err error) { + // Acquire a read log on the source node + // write lock existing node before reading treesize or tree time + lock, err := lockedfile.OpenFile(lu.MetadataBackend().LockfilePath(src), os.O_RDONLY|os.O_CREATE, 0600) + if err != nil { + return err + } + + if err != nil { + return errors.Wrap(err, "xattrs: Unable to lock source to read") + } + defer func() { + rerr := lock.Close() + + // if err is non nil we do not overwrite that + if err == nil { + err = rerr + } + }() + + return lu.CopyMetadataWithSourceLock(ctx, src, target, filter, lock, acquireTargetLock) +} + +// CopyMetadataWithSourceLock copies all extended attributes from source to target. +// The optional filter function can be used to filter by attribute name, e.g. by checking a prefix +// For the source file, a matching lockedfile is required. +// NOTE: target resource will be write locked! +func (lu *Lookup) CopyMetadataWithSourceLock(ctx context.Context, sourcePath, targetPath string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), lockedSource *lockedfile.File, acquireTargetLock bool) (err error) { + switch { + case lockedSource == nil: + return errors.New("no lock provided") + case lockedSource.File.Name() != lu.MetadataBackend().LockfilePath(sourcePath): + return errors.New("lockpath does not match filepath") + } + + attrs, err := lu.metadataBackend.All(ctx, sourcePath) + if err != nil { + return err + } + + newAttrs := make(map[string][]byte, 0) + for attrName, val := range attrs { + if filter != nil { + var ok bool + if val, ok = filter(attrName, val); !ok { + continue + } + } + newAttrs[attrName] = val + } + + return lu.MetadataBackend().SetMultiple(ctx, targetPath, newAttrs, acquireTargetLock) +} + +// GenerateSpaceID generates a space id for the given space type and owner +func (lu *Lookup) GenerateSpaceID(spaceType string, owner *user.User) (string, error) { + switch spaceType { + case _spaceTypePersonal: + return templates.WithUser(owner, lu.Options.UserLayout), nil + default: + return "", fmt.Errorf("unsupported space type: %s", spaceType) + } +} + +// DetectBackendOnDisk returns the name of the metadata backend being used on disk +func DetectBackendOnDisk(root string) string { + matches, _ := filepath.Glob(filepath.Join(root, "spaces", "*", "*")) + if len(matches) > 0 { + base := matches[len(matches)-1] + spaceid := strings.ReplaceAll( + strings.TrimPrefix(base, filepath.Join(root, "spaces")), + "/", "") + spaceRoot := Pathify(spaceid, 4, 2) + _, err := os.Stat(filepath.Join(base, "nodes", spaceRoot+".mpk")) + if err == nil { + return "mpk" + } + _, err = os.Stat(filepath.Join(base, "nodes", spaceRoot+".ini")) + if err == nil { + return "ini" + } + } + return "xattrs" +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/posix.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/posix.go new file mode 100644 index 000000000..f354f0a56 --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/posix.go @@ -0,0 +1,100 @@ +// Copyright 2018-2021 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 posix + +import ( + "fmt" + "path" + + microstore "go-micro.dev/v4/store" + + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/v2/pkg/storage" + "github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore" + "github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup" + "github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree" + "github.com/cs3org/reva/v2/pkg/storage/fs/registry" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions" + "github.com/cs3org/reva/v2/pkg/store" +) + +func init() { + registry.Register("posix", New) +} + +// New returns an implementation to of the storage.FS interface that talk to +// a local filesystem. +func New(m map[string]interface{}, stream events.Stream) (storage.FS, error) { + o, err := options.New(m) + if err != nil { + return nil, err + } + + bs, err := blobstore.New(path.Join(o.Root)) + if err != nil { + return nil, err + } + + var lu *lookup.Lookup + switch o.MetadataBackend { + case "xattrs": + lu = lookup.New(metadata.XattrsBackend{}, o) + case "messagepack": + lu = lookup.New(metadata.NewMessagePackBackend(o.Root, o.FileMetadataCache), o) + default: + return nil, fmt.Errorf("unknown metadata backend %s, only 'messagepack' or 'xattrs' (default) supported", o.MetadataBackend) + } + + tp := tree.New(lu, bs, o, store.Create( + store.Store(o.IDCache.Store), + store.TTL(o.IDCache.TTL), + store.Size(o.IDCache.Size), + microstore.Nodes(o.IDCache.Nodes...), + microstore.Database(o.IDCache.Database), + microstore.Table(o.IDCache.Table), + store.DisablePersistence(o.IDCache.DisablePersistence), + store.Authentication(o.IDCache.AuthUsername, o.IDCache.AuthPassword), + )) + + permissionsSelector, err := pool.PermissionsSelector(o.PermissionsSVC, pool.WithTLSMode(o.PermTLSMode)) + if err != nil { + return nil, err + } + + p := permissions.NewPermissions(node.NewPermissions(lu), permissionsSelector) + + aspects := aspects.Aspects{ + Lookup: lu, + Tree: tp, + Permissions: p, + EventStream: stream, + } + fs, err := decomposedfs.New(o, aspects) + if err != nil { + return nil, err + } + + return fs, nil +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree/tree.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree/tree.go new file mode 100644 index 000000000..bf6eeb70c --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree/tree.go @@ -0,0 +1,799 @@ +// Copyright 2018-2021 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 tree + +import ( + "bytes" + "context" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/appctx" + "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/google/uuid" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "go-micro.dev/v4/store" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" + "golang.org/x/sync/errgroup" +) + +var tracer trace.Tracer + +func init() { + tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/tree") +} + +//go:generate make --no-print-directory -C ../../../../.. mockery NAME=Blobstore + +// Blobstore defines an interface for storing blobs in a blobstore +type Blobstore interface { + Upload(node *node.Node, source string) error + Download(node *node.Node) (io.ReadCloser, error) + Delete(node *node.Node) error +} + +// Tree manages a hierarchical tree +type Tree struct { + lookup node.PathLookup + blobstore Blobstore + propagator propagator.Propagator + + options *options.Options + + idCache store.Store +} + +// PermissionCheckFunc defined a function used to check resource permissions +type PermissionCheckFunc func(rp *provider.ResourcePermissions) bool + +// New returns a new instance of Tree +func New(lu node.PathLookup, bs Blobstore, o *options.Options, cache store.Store) *Tree { + return &Tree{ + lookup: lu, + blobstore: bs, + options: o, + idCache: cache, + propagator: propagator.New(lu, o), + } +} + +// Setup prepares the tree structure +func (t *Tree) Setup() error { + return os.MkdirAll(t.options.Root, 0700) +} + +// GetMD returns the metadata of a node in the tree +func (t *Tree) GetMD(ctx context.Context, n *node.Node) (os.FileInfo, error) { + md, err := os.Stat(n.InternalPath()) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, errtypes.NotFound(n.ID) + } + return nil, errors.Wrap(err, "tree: error stating "+n.ID) + } + + return md, nil +} + +// TouchFile creates a new empty file +func (t *Tree) TouchFile(ctx context.Context, n *node.Node, markprocessing bool, mtime string) error { + if n.Exists { + if markprocessing { + return n.SetXattr(ctx, prefixes.StatusPrefix, []byte(node.ProcessingStatus)) + } + + return errtypes.AlreadyExists(n.ID) + } + + if n.ID == "" { + n.ID = uuid.New().String() + } + n.SetType(provider.ResourceType_RESOURCE_TYPE_FILE) + + nodePath := n.InternalPath() + if err := os.MkdirAll(filepath.Dir(nodePath), 0700); err != nil { + return errors.Wrap(err, "Decomposedfs: error creating node") + } + _, err := os.Create(nodePath) + if err != nil { + return errors.Wrap(err, "Decomposedfs: error creating node") + } + + attributes := n.NodeMetadata(ctx) + if markprocessing { + attributes[prefixes.StatusPrefix] = []byte(node.ProcessingStatus) + } + if mtime != "" { + if err := n.SetMtimeString(ctx, mtime); err != nil { + return errors.Wrap(err, "Decomposedfs: could not set mtime") + } + } else { + now := time.Now() + if err := n.SetMtime(ctx, &now); err != nil { + return errors.Wrap(err, "Decomposedfs: could not set mtime") + } + } + err = n.SetXattrsWithContext(ctx, attributes, true) + if err != nil { + return err + } + + return t.Propagate(ctx, n, 0) +} + +// CreateDir creates a new directory entry in the tree +func (t *Tree) CreateDir(ctx context.Context, n *node.Node) (err error) { + ctx, span := tracer.Start(ctx, "CreateDir") + defer span.End() + if n.Exists { + return errtypes.AlreadyExists(n.ID) // path? + } + + // create a directory node + n.SetType(provider.ResourceType_RESOURCE_TYPE_CONTAINER) + if n.ID == "" { + n.ID = uuid.New().String() + } + + err = t.createDirNode(ctx, n) + if err != nil { + return + } + + return t.Propagate(ctx, n, 0) +} + +// Move replaces the target with the source +func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node) (err error) { + if oldNode.SpaceID != newNode.SpaceID { + // WebDAV RFC https://www.rfc-editor.org/rfc/rfc4918#section-9.9.4 says to use + // > 502 (Bad Gateway) - This may occur when the destination is on another + // > server and the destination server refuses to accept the resource. + // > This could also occur when the destination is on another sub-section + // > of the same server namespace. + // but we only have a not supported error + return errtypes.NotSupported("cannot move across spaces") + } + // if target exists delete it without trashing it + if newNode.Exists { + // TODO make sure all children are deleted + if err := os.RemoveAll(newNode.InternalPath()); err != nil { + return errors.Wrap(err, "Decomposedfs: Move: error deleting target node "+newNode.ID) + } + } + + // remove cache entry in any case to avoid inconsistencies + defer func() { _ = t.idCache.Delete(filepath.Join(oldNode.ParentPath(), oldNode.Name)) }() + + // Always target the old node ID for xattr updates. + // The new node id is empty if the target does not exist + // and we need to overwrite the new one when overwriting an existing path. + // are we just renaming (parent stays the same)? + if oldNode.ParentID == newNode.ParentID { + + // parentPath := t.lookup.InternalPath(oldNode.SpaceID, oldNode.ParentID) + parentPath := oldNode.ParentPath() + + // rename child + err = os.Rename( + filepath.Join(parentPath, oldNode.Name), + filepath.Join(parentPath, newNode.Name), + ) + if err != nil { + return errors.Wrap(err, "Decomposedfs: could not rename child") + } + + // update name attribute + if err := oldNode.SetXattrString(ctx, prefixes.NameAttr, newNode.Name); err != nil { + return errors.Wrap(err, "Decomposedfs: could not set name attribute") + } + + return t.Propagate(ctx, newNode, 0) + } + + // we are moving the node to a new parent, any target has been removed + // bring old node to the new parent + + // rename child + err = os.Rename( + filepath.Join(oldNode.ParentPath(), oldNode.Name), + filepath.Join(newNode.ParentPath(), newNode.Name), + ) + if err != nil { + return errors.Wrap(err, "Decomposedfs: could not move child") + } + + // update target parentid and name + attribs := node.Attributes{} + attribs.SetString(prefixes.ParentidAttr, newNode.ParentID) + attribs.SetString(prefixes.NameAttr, newNode.Name) + if err := oldNode.SetXattrsWithContext(ctx, attribs, true); err != nil { + return errors.Wrap(err, "Decomposedfs: could not update old node attributes") + } + + // the size diff is the current treesize or blobsize of the old/source node + var sizeDiff int64 + if oldNode.IsDir(ctx) { + treeSize, err := oldNode.GetTreeSize(ctx) + if err != nil { + return err + } + sizeDiff = int64(treeSize) + } else { + sizeDiff = oldNode.Blobsize + } + + // TODO inefficient because we might update several nodes twice, only propagate unchanged nodes? + // collect in a list, then only stat each node once + // also do this in a go routine ... webdav should check the etag async + + err = t.Propagate(ctx, oldNode, -sizeDiff) + if err != nil { + return errors.Wrap(err, "Decomposedfs: Move: could not propagate old node") + } + err = t.Propagate(ctx, newNode, sizeDiff) + if err != nil { + return errors.Wrap(err, "Decomposedfs: Move: could not propagate new node") + } + return nil +} + +func readChildNodeFromLink(ctx context.Context, path string) (string, error) { + _, span := tracer.Start(ctx, "readChildNodeFromLink") + defer span.End() + link, err := os.Readlink(path) + if err != nil { + return "", err + } + nodeID := strings.TrimLeft(link, "/.") + nodeID = strings.ReplaceAll(nodeID, "/", "") + return nodeID, nil +} + +// ListFolder lists the content of a folder node +func (t *Tree) ListFolder(ctx context.Context, n *node.Node) ([]*node.Node, error) { + ctx, span := tracer.Start(ctx, "ListFolder") + defer span.End() + dir := n.InternalPath() + + _, subspan := tracer.Start(ctx, "os.Open") + f, err := os.Open(dir) + subspan.End() + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, errtypes.NotFound(dir) + } + return nil, errors.Wrap(err, "tree: error listing "+dir) + } + defer f.Close() + + _, subspan = tracer.Start(ctx, "f.Readdirnames") + names, err := f.Readdirnames(0) + subspan.End() + if err != nil { + return nil, err + } + + numWorkers := t.options.MaxConcurrency + if len(names) < numWorkers { + numWorkers = len(names) + } + work := make(chan string) + results := make(chan *node.Node) + + g, ctx := errgroup.WithContext(ctx) + + // Distribute work + g.Go(func() error { + defer close(work) + for _, name := range names { + select { + case work <- name: + case <-ctx.Done(): + return ctx.Err() + } + } + return nil + }) + + // Spawn workers that'll concurrently work the queue + for i := 0; i < numWorkers; i++ { + g.Go(func() error { + var err error + for name := range work { + path := filepath.Join(dir, name) + nodeID := getNodeIDFromCache(ctx, path, t.idCache) + if nodeID == "" { + nodeID, err = readChildNodeFromLink(ctx, path) + if err != nil { + return err + } + err = storeNodeIDInCache(ctx, path, nodeID, t.idCache) + if err != nil { + return err + } + } + + child, err := node.ReadNode(ctx, t.lookup, n.SpaceID, nodeID, false, n.SpaceRoot, true) + if err != nil { + return err + } + + // prevent listing denied resources + if !child.IsDenied(ctx) { + if child.SpaceRoot == nil { + child.SpaceRoot = n.SpaceRoot + } + select { + case results <- child: + case <-ctx.Done(): + return ctx.Err() + } + } + } + return nil + }) + } + // Wait for things to settle down, then close results chan + go func() { + _ = g.Wait() // error is checked later + close(results) + }() + + retNodes := []*node.Node{} + for n := range results { + retNodes = append(retNodes, n) + } + + if err := g.Wait(); err != nil { + return nil, err + } + + return retNodes, nil +} + +// Delete deletes a node in the tree by moving it to the trash +func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) { + path := filepath.Join(n.ParentPath(), n.Name) + // remove entry from cache immediately to avoid inconsistencies + defer func() { _ = t.idCache.Delete(path) }() + + deletingSharedResource := ctx.Value(appctx.DeletingSharedResource) + + if deletingSharedResource != nil && deletingSharedResource.(bool) { + src := filepath.Join(n.ParentPath(), n.Name) + return os.Remove(src) + } + + // get the original path + origin, err := t.lookup.Path(ctx, n, node.NoCheck) + if err != nil { + return + } + + // set origin location in metadata + nodePath := n.InternalPath() + if err := n.SetXattrString(ctx, prefixes.TrashOriginAttr, origin); err != nil { + return err + } + + var sizeDiff int64 + if n.IsDir(ctx) { + treesize, err := n.GetTreeSize(ctx) + if err != nil { + return err // TODO calculate treesize if it is not set + } + sizeDiff = -int64(treesize) + } else { + sizeDiff = -n.Blobsize + } + + deletionTime := time.Now().UTC().Format(time.RFC3339Nano) + + // Prepare the trash + trashLink := filepath.Join(t.options.Root, "spaces", lookup.Pathify(n.SpaceRoot.ID, 1, 2), "trash", lookup.Pathify(n.ID, 4, 2)) + if err := os.MkdirAll(filepath.Dir(trashLink), 0700); err != nil { + // Roll back changes + _ = n.RemoveXattr(ctx, prefixes.TrashOriginAttr, true) + return err + } + + // FIXME can we just move the node into the trash dir? instead of adding another symlink and appending a trash timestamp? + // can we just use the mtime as the trash time? + // TODO store a trashed by userid + + // first make node appear in the space trash + // parent id and name are stored as extended attributes in the node itself + err = os.Symlink("../../../../../nodes/"+lookup.Pathify(n.ID, 4, 2)+node.TrashIDDelimiter+deletionTime, trashLink) + if err != nil { + // Roll back changes + _ = n.RemoveXattr(ctx, prefixes.TrashOriginAttr, true) + return + } + + // at this point we have a symlink pointing to a non existing destination, which is fine + + // rename the trashed node so it is not picked up when traversing up the tree and matches the symlink + trashPath := nodePath + node.TrashIDDelimiter + deletionTime + err = os.Rename(nodePath, trashPath) + if err != nil { + // To roll back changes + // TODO remove symlink + // Roll back changes + _ = n.RemoveXattr(ctx, prefixes.TrashOriginAttr, true) + return + } + err = t.lookup.MetadataBackend().Rename(nodePath, trashPath) + if err != nil { + _ = n.RemoveXattr(ctx, prefixes.TrashOriginAttr, true) + _ = os.Rename(trashPath, nodePath) + return + } + + // Remove lock file if it exists + _ = os.Remove(n.LockFilePath()) + + // finally remove the entry from the parent dir + if err = os.Remove(path); err != nil { + // To roll back changes + // TODO revert the rename + // TODO remove symlink + // Roll back changes + _ = n.RemoveXattr(ctx, prefixes.TrashOriginAttr, true) + return + } + + return t.Propagate(ctx, n, sizeDiff) +} + +// RestoreRecycleItemFunc returns a node and a function to restore it from the trash. +func (t *Tree) RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPath string, targetNode *node.Node) (*node.Node, *node.Node, func() error, error) { + recycleNode, trashItem, deletedNodePath, origin, err := t.readRecycleItem(ctx, spaceid, key, trashPath) + if err != nil { + return nil, nil, nil, err + } + + targetRef := &provider.Reference{ + ResourceId: &provider.ResourceId{SpaceId: spaceid, OpaqueId: spaceid}, + Path: utils.MakeRelativePath(origin), + } + + if targetNode == nil { + targetNode, err = t.lookup.NodeFromResource(ctx, targetRef) + if err != nil { + return nil, nil, nil, err + } + } + + if err := targetNode.CheckLock(ctx); err != nil { + return nil, nil, nil, err + } + + parent, err := targetNode.Parent(ctx) + if err != nil { + return nil, nil, nil, err + } + + fn := func() error { + if targetNode.Exists { + return errtypes.AlreadyExists("origin already exists") + } + + // add the entry for the parent dir + err = os.Symlink("../../../../../"+lookup.Pathify(recycleNode.ID, 4, 2), filepath.Join(targetNode.ParentPath(), targetNode.Name)) + if err != nil { + return err + } + + // rename to node only name, so it is picked up by id + nodePath := recycleNode.InternalPath() + + // attempt to rename only if we're not in a subfolder + if deletedNodePath != nodePath { + err = os.Rename(deletedNodePath, nodePath) + if err != nil { + return err + } + err = t.lookup.MetadataBackend().Rename(deletedNodePath, nodePath) + if err != nil { + return err + } + } + + targetNode.Exists = true + + attrs := node.Attributes{} + attrs.SetString(prefixes.NameAttr, targetNode.Name) + if trashPath != "" { + // set ParentidAttr to restorePath's node parent id + attrs.SetString(prefixes.ParentidAttr, targetNode.ParentID) + } + + if err = recycleNode.SetXattrsWithContext(ctx, attrs, true); err != nil { + return errors.Wrap(err, "Decomposedfs: could not update recycle node") + } + + // delete item link in trash + deletePath := trashItem + if trashPath != "" && trashPath != "/" { + resolvedTrashRoot, err := filepath.EvalSymlinks(trashItem) + if err != nil { + return errors.Wrap(err, "Decomposedfs: could not resolve trash root") + } + deletePath = filepath.Join(resolvedTrashRoot, trashPath) + } + if err = os.Remove(deletePath); err != nil { + log.Error().Err(err).Str("trashItem", trashItem).Msg("error deleting trash item") + } + + var sizeDiff int64 + if recycleNode.IsDir(ctx) { + treeSize, err := recycleNode.GetTreeSize(ctx) + if err != nil { + return err + } + sizeDiff = int64(treeSize) + } else { + sizeDiff = recycleNode.Blobsize + } + return t.Propagate(ctx, targetNode, sizeDiff) + } + return recycleNode, parent, fn, nil +} + +// PurgeRecycleItemFunc returns a node and a function to purge it from the trash +func (t *Tree) PurgeRecycleItemFunc(ctx context.Context, spaceid, key string, path string) (*node.Node, func() error, error) { + rn, trashItem, deletedNodePath, _, err := t.readRecycleItem(ctx, spaceid, key, path) + if err != nil { + return nil, nil, err + } + + fn := func() error { + if err := t.removeNode(ctx, deletedNodePath, rn); err != nil { + return err + } + + // delete item link in trash + deletePath := trashItem + if path != "" && path != "/" { + resolvedTrashRoot, err := filepath.EvalSymlinks(trashItem) + if err != nil { + return errors.Wrap(err, "Decomposedfs: could not resolve trash root") + } + deletePath = filepath.Join(resolvedTrashRoot, path) + } + if err = os.Remove(deletePath); err != nil { + log.Error().Err(err).Str("deletePath", deletePath).Msg("error deleting trash item") + return err + } + + return nil + } + + return rn, fn, nil +} + +func (t *Tree) removeNode(ctx context.Context, path string, n *node.Node) error { + // delete the actual node + if err := utils.RemoveItem(path); err != nil { + log.Error().Err(err).Str("path", path).Msg("error purging node") + return err + } + + if err := t.lookup.MetadataBackend().Purge(path); err != nil { + log.Error().Err(err).Str("path", t.lookup.MetadataBackend().MetadataPath(path)).Msg("error purging node metadata") + return err + } + + // delete blob from blobstore + if n.BlobID != "" { + if err := t.DeleteBlob(n); err != nil { + log.Error().Err(err).Str("blobID", n.BlobID).Msg("error purging nodes blob") + return err + } + } + + // delete revisions + revs, err := filepath.Glob(n.InternalPath() + node.RevisionIDDelimiter + "*") + if err != nil { + log.Error().Err(err).Str("path", n.InternalPath()+node.RevisionIDDelimiter+"*").Msg("glob failed badly") + return err + } + for _, rev := range revs { + if t.lookup.MetadataBackend().IsMetaFile(rev) { + continue + } + + bID, err := t.lookup.ReadBlobIDAttr(ctx, rev) + if err != nil { + log.Error().Err(err).Str("revision", rev).Msg("error reading blobid attribute") + return err + } + + if err := utils.RemoveItem(rev); err != nil { + log.Error().Err(err).Str("revision", rev).Msg("error removing revision node") + return err + } + + if bID != "" { + if err := t.DeleteBlob(&node.Node{SpaceID: n.SpaceID, BlobID: bID}); err != nil { + log.Error().Err(err).Str("revision", rev).Str("blobID", bID).Msg("error removing revision node blob") + return err + } + } + + } + + return nil +} + +// Propagate propagates changes to the root of the tree +func (t *Tree) Propagate(ctx context.Context, n *node.Node, sizeDiff int64) (err error) { + return t.propagator.Propagate(ctx, n, sizeDiff) +} + +// WriteBlob writes a blob to the blobstore +func (t *Tree) WriteBlob(node *node.Node, source string) error { + return t.blobstore.Upload(node, source) +} + +// ReadBlob reads a blob from the blobstore +func (t *Tree) ReadBlob(node *node.Node) (io.ReadCloser, error) { + if node.BlobID == "" { + // there is no blob yet - we are dealing with a 0 byte file + return io.NopCloser(bytes.NewReader([]byte{})), nil + } + return t.blobstore.Download(node) +} + +// DeleteBlob deletes a blob from the blobstore +func (t *Tree) DeleteBlob(node *node.Node) error { + if node == nil { + return fmt.Errorf("could not delete blob, nil node was given") + } + if node.BlobID == "" { + return fmt.Errorf("could not delete blob, node with empty blob id was given") + } + + return t.blobstore.Delete(node) +} + +// TODO check if node exists? +func (t *Tree) createDirNode(ctx context.Context, n *node.Node) (err error) { + ctx, span := tracer.Start(ctx, "createDirNode") + defer span.End() + // create a directory node + nodePath := n.InternalPath() + if err := os.MkdirAll(nodePath, 0700); err != nil { + return errors.Wrap(err, "Decomposedfs: error creating node") + } + + attributes := n.NodeMetadata(ctx) + attributes[prefixes.TreesizeAttr] = []byte("0") // initialize as empty, TODO why bother? if it is not set we could treat it as 0? + if t.options.TreeTimeAccounting || t.options.TreeSizeAccounting { + attributes[prefixes.PropagationAttr] = []byte("1") // mark the node for propagation + } + return n.SetXattrsWithContext(ctx, attributes, true) +} + +var nodeIDRegep = regexp.MustCompile(`.*/nodes/([^.]*).*`) + +// TODO refactor the returned params into Node properties? would make all the path transformations go away... +func (t *Tree) readRecycleItem(ctx context.Context, spaceID, key, path string) (recycleNode *node.Node, trashItem string, deletedNodePath string, origin string, err error) { + if key == "" { + return nil, "", "", "", errtypes.InternalError("key is empty") + } + + backend := t.lookup.MetadataBackend() + var nodeID string + + trashItem = filepath.Join(t.lookup.InternalRoot(), "spaces", lookup.Pathify(spaceID, 1, 2), "trash", lookup.Pathify(key, 4, 2)) + resolvedTrashItem, err := filepath.EvalSymlinks(trashItem) + if err != nil { + return + } + deletedNodePath, err = filepath.EvalSymlinks(filepath.Join(resolvedTrashItem, path)) + if err != nil { + return + } + nodeID = nodeIDRegep.ReplaceAllString(deletedNodePath, "$1") + nodeID = strings.ReplaceAll(nodeID, "/", "") + + recycleNode = node.New(spaceID, nodeID, "", "", 0, "", provider.ResourceType_RESOURCE_TYPE_INVALID, nil, t.lookup) + recycleNode.SpaceRoot, err = node.ReadNode(ctx, t.lookup, spaceID, spaceID, false, nil, false) + if err != nil { + return + } + recycleNode.SetType(t.lookup.TypeFromPath(ctx, deletedNodePath)) + + var attrBytes []byte + if recycleNode.Type(ctx) == provider.ResourceType_RESOURCE_TYPE_FILE { + // lookup blobID in extended attributes + if attrBytes, err = backend.Get(ctx, deletedNodePath, prefixes.BlobIDAttr); err == nil { + recycleNode.BlobID = string(attrBytes) + } else { + return + } + + // lookup blobSize in extended attributes + if recycleNode.Blobsize, err = backend.GetInt64(ctx, deletedNodePath, prefixes.BlobsizeAttr); err != nil { + return + } + } + + // lookup parent id in extended attributes + if attrBytes, err = backend.Get(ctx, deletedNodePath, prefixes.ParentidAttr); err == nil { + recycleNode.ParentID = string(attrBytes) + } else { + return + } + + // lookup name in extended attributes + if attrBytes, err = backend.Get(ctx, deletedNodePath, prefixes.NameAttr); err == nil { + recycleNode.Name = string(attrBytes) + } else { + return + } + + // get origin node, is relative to space root + origin = "/" + + // lookup origin path in extended attributes + if attrBytes, err = backend.Get(ctx, resolvedTrashItem, prefixes.TrashOriginAttr); err == nil { + origin = filepath.Join(string(attrBytes), path) + } else { + log.Error().Err(err).Str("trashItem", trashItem).Str("deletedNodePath", deletedNodePath).Msg("could not read origin path, restoring to /") + } + + return +} + +func getNodeIDFromCache(ctx context.Context, path string, cache store.Store) string { + _, span := tracer.Start(ctx, "getNodeIDFromCache") + defer span.End() + recs, err := cache.Read(path) + if err == nil && len(recs) > 0 { + return string(recs[0].Value) + } + return "" +} + +func storeNodeIDInCache(ctx context.Context, path string, nodeID string, cache store.Store) error { + _, span := tracer.Start(ctx, "storeNodeIDInCache") + defer span.End() + return cache.Write(&store.Record{ + Key: path, + Value: []byte(nodeID), + }) +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects/aspects.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects/aspects.go new file mode 100644 index 000000000..28ee4495a --- /dev/null +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects/aspects.go @@ -0,0 +1,33 @@ +// Copyright 2018-2024 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 aspects + +import ( + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions" +) + +// Aspects holds dependencies for handling aspects of the decomposedfs +type Aspects struct { + Lookup node.PathLookup + Tree node.Tree + Permissions permissions.Permissions + EventStream events.Stream +} diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go index bedb629ee..35cc29663 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -18,16 +18,11 @@ package decomposedfs -//go:generate make --no-print-directory -C ../../../.. mockery NAME=PermissionsChecker -//go:generate make --no-print-directory -C ../../../.. mockery NAME=CS3PermissionsClient -//go:generate make --no-print-directory -C ../../../.. mockery NAME=Tree - import ( "context" "fmt" "io" "net/url" - "os" "path" "path/filepath" "strconv" @@ -47,11 +42,13 @@ import ( "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storage/cache" "github.com/cs3org/reva/v2/pkg/storage/utils/chunking" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload" @@ -83,28 +80,6 @@ func init() { tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs") } -// Tree is used to manage a tree hierarchy -type Tree interface { - Setup() error - - GetMD(ctx context.Context, node *node.Node) (os.FileInfo, error) - ListFolder(ctx context.Context, node *node.Node) ([]*node.Node, error) - // CreateHome(owner *userpb.UserId) (n *node.Node, err error) - CreateDir(ctx context.Context, node *node.Node) (err error) - TouchFile(ctx context.Context, node *node.Node, markprocessing bool, mtime string) error - // CreateReference(ctx context.Context, node *node.Node, targetURI *url.URL) error - Move(ctx context.Context, oldNode *node.Node, newNode *node.Node) (err error) - Delete(ctx context.Context, node *node.Node) (err error) - RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPath string, target *node.Node) (*node.Node, *node.Node, func() error, error) - PurgeRecycleItemFunc(ctx context.Context, spaceid, key, purgePath string) (*node.Node, func() error, error) - - WriteBlob(node *node.Node, source string) error - ReadBlob(node *node.Node) (io.ReadCloser, error) - DeleteBlob(node *node.Node) error - - Propagate(ctx context.Context, node *node.Node, sizeDiff int64) (err error) -} - // Session is the interface that OcisSession implements. By combining tus.Upload, // storage.UploadSession and custom functions we can reuse the same struct throughout // the whole upload lifecycle. @@ -127,10 +102,10 @@ type SessionStore interface { // Decomposedfs provides the base for decomposed filesystem implementations type Decomposedfs struct { - lu *lookup.Lookup - tp Tree + lu node.PathLookup + tp node.Tree o *options.Options - p Permissions + p permissions.Permissions chunkHandler *chunking.ChunkHandler stream events.Stream cache cache.StatCache @@ -175,23 +150,29 @@ func NewDefault(m map[string]interface{}, bs tree.Blobstore, es events.Stream) ( return nil, err } - permissions := NewPermissions(node.NewPermissions(lu), permissionsSelector) + aspects := aspects.Aspects{ + Lookup: lu, + Tree: tp, + Permissions: permissions.NewPermissions(node.NewPermissions(lu), permissionsSelector), + EventStream: es, + } - return New(o, lu, permissions, tp, es) + return New(o, aspects) } // New returns an implementation of the storage.FS interface that talks to // a local filesystem. -func New(o *options.Options, lu *lookup.Lookup, p Permissions, tp Tree, es events.Stream) (storage.FS, error) { +func New(o *options.Options, aspects aspects.Aspects) (storage.FS, error) { log := logger.New() - err := tp.Setup() + + err := aspects.Tree.Setup() if err != nil { log.Error().Err(err).Msg("could not setup tree") return nil, errors.Wrap(err, "could not setup tree") } // Run migrations & return - m := migrator.New(lu, log) + m := migrator.New(aspects.Lookup, log) err = m.RunMigrations() if err != nil { log.Error().Err(err).Msg("could not migrate tree") @@ -222,18 +203,18 @@ func New(o *options.Options, lu *lookup.Lookup, p Permissions, tp Tree, es event } fs := &Decomposedfs{ - tp: tp, - lu: lu, + tp: aspects.Tree, + lu: aspects.Lookup, o: o, - p: p, + p: aspects.Permissions, chunkHandler: chunking.NewChunkHandler(filepath.Join(o.Root, "uploads")), - stream: es, + stream: aspects.EventStream, cache: cache.GetStatCache(o.StatCache), UserCache: ttlcache.NewCache(), userSpaceIndex: userSpaceIndex, groupSpaceIndex: groupSpaceIndex, spaceTypeIndex: spaceTypeIndex, - sessionStore: upload.NewSessionStore(lu, tp, o.Root, es, o.AsyncFileUploads, o.Tokens), + sessionStore: upload.NewSessionStore(aspects.Lookup, aspects.Tree, o.Root, aspects.EventStream, o.AsyncFileUploads, o.Tokens), } if o.AsyncFileUploads { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup/lookup.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup/lookup.go index 9e0ffd55b..bd432c5fd 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup/lookup.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup/lookup.go @@ -25,6 +25,7 @@ import ( "path/filepath" "strings" + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/errtypes" @@ -32,6 +33,7 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" + "github.com/google/uuid" "github.com/pkg/errors" "github.com/rogpeppe/go-internal/lockedfile" "go.opentelemetry.io/otel" @@ -40,24 +42,14 @@ import ( var tracer trace.Tracer +const ( + _spaceTypePersonal = "personal" +) + func init() { tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/lookup") } -// PathLookup defines the interface for the lookup component -type PathLookup interface { - NodeFromResource(ctx context.Context, ref *provider.Reference) (*node.Node, error) - NodeFromID(ctx context.Context, id *provider.ResourceId) (n *node.Node, err error) - - InternalRoot() string - InternalPath(spaceID, nodeID string) string - Path(ctx context.Context, n *node.Node, hasPermission node.PermissionFunc) (path string, err error) - MetadataBackend() metadata.Backend - ReadBlobSizeAttr(ctx context.Context, path string) (int64, error) - ReadBlobIDAttr(ctx context.Context, path string) (string, error) - TypeFromPath(ctx context.Context, path string) provider.ResourceType -} - // Lookup implements transformations from filepath to node and back type Lookup struct { Options *options.Options @@ -200,6 +192,16 @@ func (lu *Lookup) NodeFromSpaceID(ctx context.Context, spaceID string) (n *node. return node, nil } +// GenerateSpaceID generates a new space id and alias +func (lu *Lookup) GenerateSpaceID(spaceType string, owner *user.User) (string, error) { + switch spaceType { + case _spaceTypePersonal: + return owner.Id.OpaqueId, nil + default: + return uuid.New().String(), nil + } +} + // Path returns the path for node func (lu *Lookup) Path(ctx context.Context, n *node.Node, hasPermission node.PermissionFunc) (p string, err error) { root := n.SpaceRoot diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator/migrator.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator/migrator.go index c164d6fa5..50c40b3b3 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator/migrator.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator/migrator.go @@ -25,7 +25,7 @@ import ( "path/filepath" "sort" - "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/rogpeppe/go-internal/lockedfile" "github.com/rs/zerolog" ) @@ -73,13 +73,13 @@ type Result string // Migrator runs migrations on an existing decomposedfs type Migrator struct { - lu *lookup.Lookup + lu node.PathLookup states migrationStates log *zerolog.Logger } // New returns a new Migrator instance -func New(lu *lookup.Lookup, log *zerolog.Logger) Migrator { +func New(lu node.PathLookup, log *zerolog.Logger) Migrator { return Migrator{ lu: lu, log: log, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go index 3a9f259d9..f201722da 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/node.go @@ -49,10 +49,13 @@ import ( "github.com/cs3org/reva/v2/pkg/utils" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/rogpeppe/go-internal/lockedfile" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" ) +//go:generate make --no-print-directory -C ../../../../.. mockery NAME=Tree + var tracer trace.Tracer func init() { @@ -83,6 +86,46 @@ const ( ProcessingStatus = "processing:" ) +// Tree is used to manage a tree hierarchy +type Tree interface { + Setup() error + + GetMD(ctx context.Context, node *Node) (os.FileInfo, error) + ListFolder(ctx context.Context, node *Node) ([]*Node, error) + // CreateHome(owner *userpb.UserId) (n *Node, err error) + CreateDir(ctx context.Context, node *Node) (err error) + TouchFile(ctx context.Context, node *Node, markprocessing bool, mtime string) error + // CreateReference(ctx context.Context, node *Node, targetURI *url.URL) error + Move(ctx context.Context, oldNode *Node, newNode *Node) (err error) + Delete(ctx context.Context, node *Node) (err error) + RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPath string, target *Node) (*Node, *Node, func() error, error) + PurgeRecycleItemFunc(ctx context.Context, spaceid, key, purgePath string) (*Node, func() error, error) + + WriteBlob(node *Node, source string) error + ReadBlob(node *Node) (io.ReadCloser, error) + DeleteBlob(node *Node) error + + Propagate(ctx context.Context, node *Node, sizeDiff int64) (err error) +} + +// PathLookup defines the interface for the lookup component +type PathLookup interface { + NodeFromSpaceID(ctx context.Context, spaceID string) (n *Node, err error) + NodeFromResource(ctx context.Context, ref *provider.Reference) (*Node, error) + NodeFromID(ctx context.Context, id *provider.ResourceId) (n *Node, err error) + + GenerateSpaceID(spaceType string, owner *userpb.User) (string, error) + InternalRoot() string + InternalPath(spaceID, nodeID string) string + Path(ctx context.Context, n *Node, hasPermission PermissionFunc) (path string, err error) + MetadataBackend() metadata.Backend + ReadBlobSizeAttr(ctx context.Context, path string) (int64, error) + ReadBlobIDAttr(ctx context.Context, path string) (string, error) + TypeFromPath(ctx context.Context, path string) provider.ResourceType + CopyMetadataWithSourceLock(ctx context.Context, sourcePath, targetPath string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), lockedSource *lockedfile.File, acquireTargetLock bool) (err error) + CopyMetadata(ctx context.Context, src, target string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), acquireTargetLock bool) (err error) +} + // Node represents a node in the tree and provides methods to get a Parent or Child instance type Node struct { SpaceID string @@ -100,16 +143,6 @@ type Node struct { nodeType *provider.ResourceType } -// PathLookup defines the interface for the lookup component -type PathLookup interface { - InternalRoot() string - InternalPath(spaceID, nodeID string) string - Path(ctx context.Context, n *Node, hasPermission PermissionFunc) (path string, err error) - MetadataBackend() metadata.Backend - ReadBlobSizeAttr(ctx context.Context, path string) (int64, error) - ReadBlobIDAttr(ctx context.Context, path string) (string, error) -} - // New returns a new instance of Node func New(spaceID, id, parentID, name string, blobsize int64, blobID string, t provider.ResourceType, owner *userpb.UserId, lu PathLookup) *Node { if blobID == "" { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions/spacepermissions.go similarity index 92% rename from vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go rename to vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions/spacepermissions.go index 61c42e4bc..7de6144d3 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spacepermissions.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions/spacepermissions.go @@ -1,4 +1,4 @@ -package decomposedfs +package permissions import ( "context" @@ -11,9 +11,27 @@ import ( "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/utils" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" ) +//go:generate make --no-print-directory -C ../../../../.. mockery NAME=PermissionsChecker +//go:generate make --no-print-directory -C ../../../../.. mockery NAME=CS3PermissionsClient + +var ( + tracer trace.Tracer +) + +func init() { + tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/permissions") +} + +const ( + _spaceTypePersonal = "personal" + _spaceTypeProject = "project" +) + // PermissionsChecker defines an interface for checking permissions on a Node type PermissionsChecker interface { AssemblePermissions(ctx context.Context, n *node.Node) (ap provider.ResourcePermissions, err error) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex/spaceidindex.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex/spaceidindex.go index 4d9add389..d9dc9c359 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex/spaceidindex.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex/spaceidindex.go @@ -50,10 +50,16 @@ func (i *Index) Load(index string) (map[string]string, error) { } // Add adds an entry to an index +// Consider calling AddAll() when trying to add multiple entries as every Add call has to lock the index func (i *Index) Add(index, key string, value string) error { return i.updateIndex(index, map[string]string{key: value}, []string{}) } +// AddAll adds multiple entries to the index +func (i *Index) AddAll(index string, m map[string]string) error { + return i.updateIndex(index, m, []string{}) +} + // Remove removes an entry from the index func (i *Index) Remove(index, key string) error { return i.updateIndex(index, map[string]string{}, []string{key}) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go index a3745492d..0c1f541f8 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go @@ -44,11 +44,11 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions" "github.com/cs3org/reva/v2/pkg/storage/utils/filelocks" "github.com/cs3org/reva/v2/pkg/storage/utils/templates" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/google/uuid" "github.com/pkg/errors" "golang.org/x/sync/errgroup" ) @@ -66,9 +66,13 @@ const ( // CreateStorageSpace creates a storage space func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { ctx = context.WithValue(ctx, utils.SpaceGrant, struct{}{}) + u := ctxpkg.ContextMustGetUser(ctx) // "everything is a resource" this is the unique ID for the Space resource. - spaceID := uuid.New().String() + spaceID, err := fs.lu.GenerateSpaceID(req.Type, req.GetOwner()) + if err != nil { + return nil, err + } // allow sending a space id if reqSpaceID := utils.ReadPlainFromOpaque(req.Opaque, "spaceid"); reqSpaceID != "" { spaceID = reqSpaceID @@ -77,14 +81,12 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr description := utils.ReadPlainFromOpaque(req.Opaque, "description") // allow sending a spaceAlias alias := utils.ReadPlainFromOpaque(req.Opaque, "spaceAlias") - u := ctxpkg.ContextMustGetUser(ctx) if alias == "" { alias = templates.WithSpacePropertiesAndUser(u, req.Type, req.Name, fs.o.GeneralSpaceAliasTemplate) } // TODO enforce a uuid? // TODO clarify if we want to enforce a single personal storage space or if we want to allow sending the spaceid if req.Type == _spaceTypePersonal { - spaceID = req.GetOwner().GetId().GetOpaqueId() alias = templates.WithSpacePropertiesAndUser(u, req.Type, req.Name, fs.o.PersonalSpaceAliasTemplate) } @@ -620,7 +622,7 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up } - if !restore && len(metadata) == 0 && !IsViewer(sp) { + if !restore && len(metadata) == 0 && !permissions.IsViewer(sp) { // you may land here when making an update request without changes // check if user has access to the drive before continuing return &provider.UpdateStorageSpaceResponse{ @@ -628,10 +630,10 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up }, nil } - if !IsManager(sp) { + if !permissions.IsManager(sp) { // We are not a space manager. We need to check for additional permissions. k := []string{prefixes.NameAttr, prefixes.SpaceDescriptionAttr} - if !IsEditor(sp) { + if !permissions.IsEditor(sp) { k = append(k, prefixes.SpaceReadmeAttr, prefixes.SpaceAliasAttr, prefixes.SpaceImageAttr) } @@ -800,7 +802,7 @@ func (fs *Decomposedfs) storageSpaceFromNode(ctx context.Context, n *node.Node, if n.SpaceRoot.IsDisabled(ctx) { rp, err := fs.p.AssemblePermissions(ctx, n) - if err != nil || !IsManager(rp) { + if err != nil || !permissions.IsManager(rp) { return nil, errtypes.PermissionDenied(fmt.Sprintf("user %s is not allowed to list deleted spaces %s", user.Username, n.ID)) } } @@ -1060,7 +1062,7 @@ func (fs *Decomposedfs) getSpaceRoot(spaceID string) string { // - a user with the "delete-all-spaces" permission may delete but not enable/disable any project space // - a user with the "Drive.ReadWriteEnabled" permission may enable/disable but not delete any project space // - a project space can always be enabled/disabled/deleted by its manager (i.e. users have the "remove" grant) -func canDeleteSpace(ctx context.Context, spaceID string, typ string, purge bool, n *node.Node, p Permissions) error { +func canDeleteSpace(ctx context.Context, spaceID string, typ string, purge bool, n *node.Node, p permissions.Permissions) error { // delete-all-home spaces allows to disable and delete a personal space if typ == "personal" { if p.DeleteAllHomeSpaces(ctx) { @@ -1070,7 +1072,7 @@ func canDeleteSpace(ctx context.Context, spaceID string, typ string, purge bool, } // space managers are allowed to disable and delete their project spaces - if rp, err := p.AssemblePermissions(ctx, n); err == nil && IsManager(rp) { + if rp, err := p.AssemblePermissions(ctx, n); err == nil && permissions.IsManager(rp) { return nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/async.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/async.go index 3bd6864d9..2f55b1b99 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/async.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/async.go @@ -48,7 +48,7 @@ type AsyncPropagator struct { treeSizeAccounting bool treeTimeAccounting bool propagationDelay time.Duration - lookup lookup.PathLookup + lookup node.PathLookup } // Change represents a change to the tree @@ -58,7 +58,7 @@ type Change struct { } // NewAsyncPropagator returns a new AsyncPropagator instance -func NewAsyncPropagator(treeSizeAccounting, treeTimeAccounting bool, o options.AsyncPropagatorOptions, lookup lookup.PathLookup) AsyncPropagator { +func NewAsyncPropagator(treeSizeAccounting, treeTimeAccounting bool, o options.AsyncPropagatorOptions, lookup node.PathLookup) AsyncPropagator { p := AsyncPropagator{ treeSizeAccounting: treeSizeAccounting, treeTimeAccounting: treeTimeAccounting, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/propagator.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/propagator.go index 6f12e43c6..008e8d988 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/propagator.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/propagator.go @@ -26,7 +26,6 @@ import ( sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/appctx" - "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options" @@ -45,7 +44,7 @@ type Propagator interface { Propagate(ctx context.Context, node *node.Node, sizediff int64) error } -func New(lookup lookup.PathLookup, o *options.Options) Propagator { +func New(lookup node.PathLookup, o *options.Options) Propagator { switch o.Propagator { case "async": return NewAsyncPropagator(o.TreeSizeAccounting, o.TreeTimeAccounting, o.AsyncPropagatorOptions, lookup) @@ -54,7 +53,7 @@ func New(lookup lookup.PathLookup, o *options.Options) Propagator { } } -func calculateTreeSize(ctx context.Context, lookup lookup.PathLookup, childrenPath string) (uint64, error) { +func calculateTreeSize(ctx context.Context, lookup node.PathLookup, childrenPath string) (uint64, error) { ctx, span := tracer.Start(ctx, "calculateTreeSize") defer span.End() var size uint64 diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/sync.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/sync.go index 4e03aada6..f3729cb97 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/sync.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator/sync.go @@ -26,7 +26,6 @@ import ( "time" "github.com/cs3org/reva/v2/pkg/appctx" - "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" @@ -37,11 +36,11 @@ import ( type SyncPropagator struct { treeSizeAccounting bool treeTimeAccounting bool - lookup lookup.PathLookup + lookup node.PathLookup } // NewSyncPropagator returns a new AsyncPropagator instance -func NewSyncPropagator(treeSizeAccounting, treeTimeAccounting bool, lookup lookup.PathLookup) SyncPropagator { +func NewSyncPropagator(treeSizeAccounting, treeTimeAccounting bool, lookup node.PathLookup) SyncPropagator { return SyncPropagator{ treeSizeAccounting: treeSizeAccounting, treeTimeAccounting: treeTimeAccounting, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/tree.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/tree.go index dc8643fd2..e6d108c0e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/tree.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/tree.go @@ -66,7 +66,7 @@ type Blobstore interface { // Tree manages a hierarchical tree type Tree struct { - lookup lookup.PathLookup + lookup node.PathLookup blobstore Blobstore propagator propagator.Propagator @@ -79,7 +79,7 @@ type Tree struct { type PermissionCheckFunc func(rp *provider.ResourcePermissions) bool // New returns a new instance of Tree -func New(lu lookup.PathLookup, bs Blobstore, o *options.Options, cache store.Store) *Tree { +func New(lu node.PathLookup, bs Blobstore, o *options.Options, cache store.Store) *Tree { return &Tree{ lookup: lu, blobstore: bs, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload/store.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload/store.go index def5e545f..57a7aea8f 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload/store.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload/store.go @@ -53,7 +53,7 @@ type PermissionsChecker interface { // OcisStore manages upload sessions type OcisStore struct { - lu *lookup.Lookup + lu node.PathLookup tp Tree root string pub events.Publisher @@ -62,7 +62,7 @@ type OcisStore struct { } // NewSessionStore returns a new OcisStore -func NewSessionStore(lu *lookup.Lookup, tp Tree, root string, pub events.Publisher, async bool, tknopts options.TokenOptions) *OcisStore { +func NewSessionStore(lu node.PathLookup, tp Tree, root string, pub events.Publisher, async bool, tknopts options.TokenOptions) *OcisStore { return &OcisStore{ lu: lu, tp: tp, diff --git a/vendor/github.com/go-playground/validator/v10/Makefile b/vendor/github.com/go-playground/validator/v10/Makefile index 09f171ba1..8bf4f28ab 100644 --- a/vendor/github.com/go-playground/validator/v10/Makefile +++ b/vendor/github.com/go-playground/validator/v10/Makefile @@ -1,4 +1,4 @@ -GOCMD=GO111MODULE=on go +GOCMD=go linters-install: @golangci-lint --version >/dev/null 2>&1 || { \ diff --git a/vendor/github.com/go-playground/validator/v10/README.md b/vendor/github.com/go-playground/validator/v10/README.md index 810097b3a..b3e113a19 100644 --- a/vendor/github.com/go-playground/validator/v10/README.md +++ b/vendor/github.com/go-playground/validator/v10/README.md @@ -1,7 +1,7 @@ Package validator ================= [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -![Project status](https://img.shields.io/badge/version-10.17.0-green.svg) +![Project status](https://img.shields.io/badge/version-10.18.0-green.svg) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) diff --git a/vendor/github.com/klauspost/compress/flate/deflate.go b/vendor/github.com/klauspost/compress/flate/deflate.go index de912e187..66d1657d2 100644 --- a/vendor/github.com/klauspost/compress/flate/deflate.go +++ b/vendor/github.com/klauspost/compress/flate/deflate.go @@ -212,7 +212,7 @@ func (d *compressor) writeBlockSkip(tok *tokens, index int, eof bool) error { // Should only be used after a start/reset. func (d *compressor) fillWindow(b []byte) { // Do not fill window if we are in store-only or huffman mode. - if d.level <= 0 { + if d.level <= 0 && d.level > -MinCustomWindowSize { return } if d.fast != nil { diff --git a/vendor/github.com/klauspost/compress/internal/race/norace.go b/vendor/github.com/klauspost/compress/internal/race/norace.go new file mode 100644 index 000000000..affbbbb59 --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/race/norace.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !race + +package race + +func ReadSlice[T any](s []T) { +} + +func WriteSlice[T any](s []T) { +} diff --git a/vendor/github.com/klauspost/compress/internal/race/race.go b/vendor/github.com/klauspost/compress/internal/race/race.go new file mode 100644 index 000000000..f5e240dcd --- /dev/null +++ b/vendor/github.com/klauspost/compress/internal/race/race.go @@ -0,0 +1,26 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build race + +package race + +import ( + "runtime" + "unsafe" +) + +func ReadSlice[T any](s []T) { + if len(s) == 0 { + return + } + runtime.RaceReadRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) +} + +func WriteSlice[T any](s []T) { + if len(s) == 0 { + return + } + runtime.RaceWriteRange(unsafe.Pointer(&s[0]), len(s)*int(unsafe.Sizeof(s[0]))) +} diff --git a/vendor/github.com/klauspost/compress/s2/decode.go b/vendor/github.com/klauspost/compress/s2/decode.go index 6c7feafcc..264ffd0a9 100644 --- a/vendor/github.com/klauspost/compress/s2/decode.go +++ b/vendor/github.com/klauspost/compress/s2/decode.go @@ -10,6 +10,8 @@ import ( "errors" "fmt" "strconv" + + "github.com/klauspost/compress/internal/race" ) var ( @@ -63,6 +65,10 @@ func Decode(dst, src []byte) ([]byte, error) { } else { dst = make([]byte, dLen) } + + race.WriteSlice(dst) + race.ReadSlice(src[s:]) + if s2Decode(dst, src[s:]) != 0 { return nil, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/s2/encode_amd64.go b/vendor/github.com/klauspost/compress/s2/encode_amd64.go index ebc332ad5..4f45206a4 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_amd64.go +++ b/vendor/github.com/klauspost/compress/s2/encode_amd64.go @@ -3,6 +3,8 @@ package s2 +import "github.com/klauspost/compress/internal/race" + const hasAmd64Asm = true // encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It @@ -14,6 +16,9 @@ const hasAmd64Asm = true // len(dst) >= MaxEncodedLen(len(src)) && // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlock(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + const ( // Use 12 bit table when less than... limit12B = 16 << 10 @@ -50,6 +55,9 @@ func encodeBlock(dst, src []byte) (d int) { // len(dst) >= MaxEncodedLen(len(src)) && // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlockBetter(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + const ( // Use 12 bit table when less than... limit12B = 16 << 10 @@ -86,6 +94,9 @@ func encodeBlockBetter(dst, src []byte) (d int) { // len(dst) >= MaxEncodedLen(len(src)) && // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlockSnappy(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + const ( // Use 12 bit table when less than... limit12B = 16 << 10 @@ -121,6 +132,9 @@ func encodeBlockSnappy(dst, src []byte) (d int) { // len(dst) >= MaxEncodedLen(len(src)) && // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlockBetterSnappy(dst, src []byte) (d int) { + race.ReadSlice(src) + race.WriteSlice(dst) + const ( // Use 12 bit table when less than... limit12B = 16 << 10 diff --git a/vendor/github.com/klauspost/compress/s2/reader.go b/vendor/github.com/klauspost/compress/s2/reader.go index 2f01a3987..46ead58fe 100644 --- a/vendor/github.com/klauspost/compress/s2/reader.go +++ b/vendor/github.com/klauspost/compress/s2/reader.go @@ -104,12 +104,14 @@ func ReaderIgnoreStreamIdentifier() ReaderOption { // For each chunk with the ID, the callback is called with the content. // Any returned non-nil error will abort decompression. // Only one callback per ID is supported, latest sent will be used. +// You can peek the stream, triggering the callback, by doing a Read with a 0 +// byte buffer. func ReaderSkippableCB(id uint8, fn func(r io.Reader) error) ReaderOption { return func(r *Reader) error { if id < 0x80 || id > 0xfd { return fmt.Errorf("ReaderSkippableCB: Invalid id provided, must be 0x80-0xfd (inclusive)") } - r.skippableCB[id] = fn + r.skippableCB[id-0x80] = fn return nil } } @@ -128,7 +130,7 @@ type Reader struct { err error decoded []byte buf []byte - skippableCB [0x80]func(r io.Reader) error + skippableCB [0xff - 0x80]func(r io.Reader) error blockStart int64 // Uncompressed offset at start of current. index *Index @@ -201,7 +203,7 @@ func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { // The supplied slice does not need to be the size of the read. func (r *Reader) skippable(tmp []byte, n int, allowEOF bool, id uint8) (ok bool) { if id < 0x80 { - r.err = fmt.Errorf("interbal error: skippable id < 0x80") + r.err = fmt.Errorf("internal error: skippable id < 0x80") return false } if fn := r.skippableCB[id-0x80]; fn != nil { @@ -1048,15 +1050,17 @@ func (r *Reader) ReadByte() (byte, error) { } // SkippableCB will register a callback for chunks with the specified ID. -// ID must be a Reserved skippable chunks ID, 0x80-0xfe (inclusive). +// ID must be a Reserved skippable chunks ID, 0x80-0xfd (inclusive). // For each chunk with the ID, the callback is called with the content. // Any returned non-nil error will abort decompression. // Only one callback per ID is supported, latest sent will be used. // Sending a nil function will disable previous callbacks. +// You can peek the stream, triggering the callback, by doing a Read with a 0 +// byte buffer. func (r *Reader) SkippableCB(id uint8, fn func(r io.Reader) error) error { - if id < 0x80 || id > chunkTypePadding { + if id < 0x80 || id >= chunkTypePadding { return fmt.Errorf("ReaderSkippableCB: Invalid id provided, must be 0x80-0xfe (inclusive)") } - r.skippableCB[id] = fn + r.skippableCB[id-0x80] = fn return nil } diff --git a/vendor/github.com/klauspost/compress/s2/s2.go b/vendor/github.com/klauspost/compress/s2/s2.go index dae3f731f..72bcb4945 100644 --- a/vendor/github.com/klauspost/compress/s2/s2.go +++ b/vendor/github.com/klauspost/compress/s2/s2.go @@ -37,6 +37,8 @@ package s2 import ( "bytes" "hash/crc32" + + "github.com/klauspost/compress/internal/race" ) /* @@ -112,6 +114,8 @@ var crcTable = crc32.MakeTable(crc32.Castagnoli) // crc implements the checksum specified in section 3 of // https://github.com/google/snappy/blob/master/framing_format.txt func crc(b []byte) uint32 { + race.ReadSlice(b) + c := crc32.Update(0, crcTable, b) return c>>15 | c<<17 + 0xa282ead8 } diff --git a/vendor/github.com/klauspost/compress/s2/writer.go b/vendor/github.com/klauspost/compress/s2/writer.go index 089cd36d8..bba66a876 100644 --- a/vendor/github.com/klauspost/compress/s2/writer.go +++ b/vendor/github.com/klauspost/compress/s2/writer.go @@ -13,6 +13,8 @@ import ( "io" "runtime" "sync" + + "github.com/klauspost/compress/internal/race" ) const ( @@ -271,7 +273,7 @@ func (w *Writer) AddSkippableBlock(id uint8, data []byte) (err error) { return fmt.Errorf("skippable block excessed maximum size") } var header [4]byte - chunkLen := 4 + len(data) + chunkLen := len(data) header[0] = id header[1] = uint8(chunkLen >> 0) header[2] = uint8(chunkLen >> 8) @@ -282,7 +284,7 @@ func (w *Writer) AddSkippableBlock(id uint8, data []byte) (err error) { if err = w.err(err); err != nil { return err } - if n != len(data) { + if n != len(b) { return w.err(io.ErrShortWrite) } w.written += int64(n) @@ -303,9 +305,7 @@ func (w *Writer) AddSkippableBlock(id uint8, data []byte) (err error) { if err := write(header[:]); err != nil { return err } - if err := write(data); err != nil { - return err - } + return write(data) } // Create output... @@ -385,6 +385,8 @@ func (w *Writer) EncodeBuffer(buf []byte) (err error) { buf = buf[len(uncompressed):] // Get an output buffer. obuf := w.buffers.Get().([]byte)[:len(uncompressed)+obufHeaderLen] + race.WriteSlice(obuf) + output := make(chan result) // Queue output now, so we keep order. w.output <- output @@ -393,6 +395,8 @@ func (w *Writer) EncodeBuffer(buf []byte) (err error) { } w.uncompWritten += int64(len(uncompressed)) go func() { + race.ReadSlice(uncompressed) + checksum := crc(uncompressed) // Set to uncompressed. diff --git a/vendor/github.com/leodido/go-urn/.gitignore b/vendor/github.com/leodido/go-urn/.gitignore index 89d4bc55d..427454f8f 100644 --- a/vendor/github.com/leodido/go-urn/.gitignore +++ b/vendor/github.com/leodido/go-urn/.gitignore @@ -9,4 +9,5 @@ *.txt vendor/ -/removecomments \ No newline at end of file +/removecomments +/snake2camel \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/README.md b/vendor/github.com/leodido/go-urn/README.md index 731eecbb5..619475bfb 100644 --- a/vendor/github.com/leodido/go-urn/README.md +++ b/vendor/github.com/leodido/go-urn/README.md @@ -2,21 +2,34 @@ **A parser for URNs**. -> As seen on [RFC 2141](https://tools.ietf.org/html/rfc2141#ref-1). +> As seen on [RFC 2141](https://datatracker.ietf.org/doc/html/rfc2141), [RFC 7643](https://datatracker.ietf.org/doc/html/rfc7643#section-10), and on [RFC 8141](https://datatracker.ietf.org/doc/html/rfc8141). [API documentation](https://godoc.org/github.com/leodido/go-urn). +Starting with version 1.3 this library also supports [RFC 7643 SCIM URNs](https://datatracker.ietf.org/doc/html/rfc7643#section-10). + +Starting with version 1.4 this library also supports [RFC 8141 URNs (2017)](https://datatracker.ietf.org/doc/html/rfc8141). + ## Installation ``` go get github.com/leodido/go-urn ``` +## Features + +1. RFC 2141 URNs parsing (default) +2. RFC 8141 URNs parsing (supersedes RFC 2141) +3. RFC 7643 SCIM URNs parsing +4. Normalization as per RFCs +5. Lexical equivalence as per RFCs +6. Precise, fine-grained errors + ## Performances This implementation results to be really fast. -Usually below ½ microsecond on my machine[1](#mymachine). +Usually below 400 ns on my machine[1](#mymachine). Notice it also performs, while parsing: @@ -24,35 +37,36 @@ Notice it also performs, while parsing: 2. specific-string normalization ``` -ok/00/urn:a:b______________________________________/-4 20000000 265 ns/op 182 B/op 6 allocs/op -ok/01/URN:foo:a123,456_____________________________/-4 30000000 296 ns/op 200 B/op 6 allocs/op -ok/02/urn:foo:a123%2c456___________________________/-4 20000000 331 ns/op 208 B/op 6 allocs/op -ok/03/urn:ietf:params:scim:schemas:core:2.0:User___/-4 20000000 430 ns/op 280 B/op 6 allocs/op -ok/04/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 411 ns/op 312 B/op 6 allocs/op -ok/05/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 472 ns/op 344 B/op 6 allocs/op -ok/06/urn:burnout:nss______________________________/-4 30000000 257 ns/op 192 B/op 6 allocs/op -ok/07/urn:abcdefghilmnopqrstuvzabcdefghilm:x_______/-4 20000000 375 ns/op 213 B/op 6 allocs/op -ok/08/urn:urnurnurn:urn____________________________/-4 30000000 265 ns/op 197 B/op 6 allocs/op -ok/09/urn:ciao:@!=%2c(xyz)+a,b.*@g=$_'_____________/-4 20000000 307 ns/op 248 B/op 6 allocs/op -ok/10/URN:x:abc%1dz%2f%3az_________________________/-4 30000000 259 ns/op 212 B/op 6 allocs/op -no/11/URN:-xxx:x___________________________________/-4 20000000 445 ns/op 320 B/op 6 allocs/op -no/12/urn::colon:nss_______________________________/-4 20000000 461 ns/op 320 B/op 6 allocs/op -no/13/urn:abcdefghilmnopqrstuvzabcdefghilmn:specifi/-4 10000000 660 ns/op 320 B/op 6 allocs/op -no/14/URN:a!?:x____________________________________/-4 20000000 507 ns/op 320 B/op 6 allocs/op -no/15/urn:urn:NSS__________________________________/-4 20000000 429 ns/op 288 B/op 6 allocs/op -no/16/urn:white_space:NSS__________________________/-4 20000000 482 ns/op 320 B/op 6 allocs/op -no/17/urn:concat:no_spaces_________________________/-4 20000000 539 ns/op 328 B/op 7 allocs/op -no/18/urn:a:/______________________________________/-4 20000000 470 ns/op 320 B/op 7 allocs/op -no/19/urn:UrN:NSS__________________________________/-4 20000000 399 ns/op 288 B/op 6 allocs/op +ok/00/urn:a:b______________________________________/-10 51372006 109.0 ns/op 275 B/op 3 allocs/op +ok/01/URN:foo:a123,456_____________________________/-10 36024072 160.8 ns/op 296 B/op 6 allocs/op +ok/02/urn:foo:a123%2C456___________________________/-10 31901007 188.4 ns/op 320 B/op 7 allocs/op +ok/03/urn:ietf:params:scim:schemas:core:2.0:User___/-10 22736756 266.6 ns/op 376 B/op 6 allocs/op +ok/04/urn:ietf:params:scim:schemas:extension:enterp/-10 18291859 335.2 ns/op 408 B/op 6 allocs/op +ok/05/urn:ietf:params:scim:schemas:extension:enterp/-10 15283087 379.4 ns/op 440 B/op 6 allocs/op +ok/06/urn:burnout:nss______________________________/-10 39407593 155.1 ns/op 288 B/op 6 allocs/op +ok/07/urn:abcdefghilmnopqrstuvzabcdefghilm:x_______/-10 27832718 211.4 ns/op 307 B/op 4 allocs/op +ok/08/urn:urnurnurn:urn____________________________/-10 33269596 168.1 ns/op 293 B/op 6 allocs/op +ok/09/urn:ciao:!!*_________________________________/-10 41100675 148.8 ns/op 288 B/op 6 allocs/op +ok/10/urn:ciao:=@__________________________________/-10 37214253 149.7 ns/op 284 B/op 6 allocs/op +ok/11/urn:ciao:@!=%2C(xyz)+a,b.*@g=$_'_____________/-10 26534240 229.8 ns/op 336 B/op 7 allocs/op +ok/12/URN:x:abc%1Dz%2F%3az_________________________/-10 28166396 211.8 ns/op 336 B/op 7 allocs/op +no/13/URN:---xxx:x_________________________________/-10 23635159 255.6 ns/op 419 B/op 5 allocs/op +no/14/urn::colon:nss_______________________________/-10 23594779 258.4 ns/op 419 B/op 5 allocs/op +no/15/URN:@,:x_____________________________________/-10 23742535 261.5 ns/op 419 B/op 5 allocs/op +no/16/URN:URN:NSS__________________________________/-10 27432714 223.3 ns/op 371 B/op 5 allocs/op +no/17/urn:UrN:NSS__________________________________/-10 26922117 224.9 ns/op 371 B/op 5 allocs/op +no/18/urn:a:%______________________________________/-10 24926733 224.6 ns/op 371 B/op 5 allocs/op +no/19/urn:urn:NSS__________________________________/-10 27652641 220.7 ns/op 371 B/op 5 allocs/op ``` ---- +* [1]: Apple M1 Pro -* [1]: Intel Core i7-7600U CPU @ 2.80GHz - ---- ## Example + +For more examples take a look at the [examples file](examples_test.go). + + ```go package main @@ -64,6 +78,35 @@ import ( func main() { var uid = "URN:foo:a123,456" + // Parse the input string as a RFC 2141 URN only + u, e := urn.NewMachine().Parse(uid) + if e != nil { + fmt.Errorf(err) + + return + } + + fmt.Println(u.ID) + fmt.Println(u.SS) + + // Output: + // foo + // a123,456 +} +``` + +```go +package main + +import ( + "fmt" + "github.com/leodido/go-urn" +) + +func main() { + var uid = "URN:foo:a123,456" + + // Parse the input string as a RFC 2141 URN only u, ok := urn.Parse([]byte(uid)) if !ok { panic("error parsing urn") @@ -78,4 +121,33 @@ func main() { } ``` -[![Analytics](https://ga-beacon.appspot.com/UA-49657176-1/go-urn?flat)](https://github.com/igrigorik/ga-beacon) \ No newline at end of file +```go +package main + +import ( + "fmt" + "github.com/leodido/go-urn" +) + +func main() { + input := "urn:ietf:params:scim:api:messages:2.0:ListResponse" + + // Parsing the input string as a RFC 7643 SCIM URN + u, ok := urn.Parse([]byte(input), urn.WithParsingMode(urn.RFC7643Only)) + if !ok { + panic("error parsing urn") + } + + fmt.Println(u.IsSCIM()) + scim := u.SCIM() + fmt.Println(scim.Type.String()) + fmt.Println(scim.Name) + fmt.Println(scim.Other) + + // Output: + // true + // api + // messages + // 2.0:ListResponse +} +``` \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/kind.go b/vendor/github.com/leodido/go-urn/kind.go new file mode 100644 index 000000000..f5e140f0a --- /dev/null +++ b/vendor/github.com/leodido/go-urn/kind.go @@ -0,0 +1,10 @@ +package urn + +type Kind int + +const ( + NONE Kind = iota + RFC2141 + RFC7643 + RFC8141 +) diff --git a/vendor/github.com/leodido/go-urn/machine.go b/vendor/github.com/leodido/go-urn/machine.go index f8d57b412..aec1ba69c 100644 --- a/vendor/github.com/leodido/go-urn/machine.go +++ b/vendor/github.com/leodido/go-urn/machine.go @@ -2,27 +2,98 @@ package urn import ( "fmt" + + scimschema "github.com/leodido/go-urn/scim/schema" ) var ( - errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" - errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" - errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" - errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" - errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" - errParse = "parsing error [col %d]" + errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" + errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its beginning) [col %d]" + errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" + errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" + errHex = "expecting the percent encoded chars to be well-formed (%%alnum{2}) [col %d]" + errSCIMNamespace = "expecing the SCIM namespace identifier (ietf:params:scim) [col %d]" + errSCIMType = "expecting a correct SCIM type (schemas, api, param) [col %d]" + errSCIMName = "expecting one or more alnum char in the SCIM name part [col %d]" + errSCIMOther = "expecting a well-formed other SCIM part [col %d]" + errSCIMOtherIncomplete = "expecting a not empty SCIM other part after colon [col %d]" + err8141InformalID = "informal URN namespace must be in the form urn-[1-9][0-9] [col %d]" + err8141SpecificString = "expecting the specific string to contain alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] not in first position) chars [col %d]" + err8141Identifier = "expecting the indentifier to be a string with (length 2 to 32 chars) containing alnum (or dashes) not starting or ending with a dash [col %d]" + err8141RComponentStart = "expecting only one r-component (starting with the ?+ sequence) [col %d]" + err8141QComponentStart = "expecting only one q-component (starting with the ?= sequence) [col %d]" + err8141MalformedRComp = "expecting a non-empty r-component containing alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] but not at its beginning) [col %d]" + err8141MalformedQComp = "expecting a non-empty q-component containing alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] but not at its beginning) [col %d]" ) +var _toStateActions []byte = []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, +} + +var _eofActions []byte = []byte{ + 0, 1, 1, 1, 1, 4, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 8, 9, + 9, 4, 4, 11, 1, 1, 1, 1, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 14, 14, 14, 14, 16, 18, 20, + 20, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 23, 24, 24, 25, 25, 0, 26, 28, + 28, 29, 29, 30, 30, 26, 26, 31, + 31, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 21, + 21, 22, 22, 22, 34, 34, 35, 37, + 37, 38, 40, 41, 41, 38, 42, 42, + 42, 44, 42, 48, 48, 48, 50, 44, + 50, 0, +} const start int = 1 -const firstFinal int = 44 +const firstFinal int = 172 -const enFail int = 46 +const enScimOnly int = 44 +const enRfc8141Only int = 83 +const enFail int = 193 const enMain int = 1 // Machine is the interface representing the FSM type Machine interface { Error() error Parse(input []byte) (*URN, error) + WithParsingMode(ParsingMode) } type machine struct { @@ -30,12 +101,24 @@ type machine struct { cs int p, pe, eof, pb int err error - tolower []int + startParsingAt int + parsingMode ParsingMode + parsingModeSet bool } // NewMachine creates a new FSM able to parse RFC 2141 strings. -func NewMachine() Machine { - m := &machine{} +func NewMachine(options ...Option) Machine { + m := &machine{ + parsingModeSet: false, + } + + for _, o := range options { + o(m) + } + // Set default parsing mode + if !m.parsingModeSet { + m.WithParsingMode(DefaultParsingMode) + } return m } @@ -51,7 +134,7 @@ func (m *machine) text() []byte { return m.data[m.pb:m.p] } -// Parse parses the input byte array as a RFC 2141 string. +// Parse parses the input byte array as a RFC 2141 or RFC7643 string. func (m *machine) Parse(input []byte) (*URN, error) { m.data = input m.p = 0 @@ -59,1619 +142,4881 @@ func (m *machine) Parse(input []byte) (*URN, error) { m.pe = len(input) m.eof = len(input) m.err = nil - m.tolower = []int{} - output := &URN{} - { - m.cs = start + m.cs = m.startParsingAt + output := &URN{ + tolower: []int{}, } { if (m.p) == (m.pe) { goto _testEof } + if m.cs == 0 { + goto _out + } + _resume: switch m.cs { case 1: - goto stCase1 + switch (m.data)[(m.p)] { + case 85: + goto tr1 + case 117: + goto tr1 + } + goto tr0 case 0: - goto stCase0 + goto _out case 2: - goto stCase2 + switch (m.data)[(m.p)] { + case 82: + goto tr2 + case 114: + goto tr2 + } + goto tr0 case 3: - goto stCase3 + switch (m.data)[(m.p)] { + case 78: + goto tr3 + case 110: + goto tr3 + } + goto tr0 case 4: - goto stCase4 + if (m.data)[(m.p)] == 58 { + goto tr4 + } + goto tr0 case 5: - goto stCase5 + switch (m.data)[(m.p)] { + case 85: + goto tr7 + case 117: + goto tr7 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr6 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr6 + } + default: + goto tr6 + } + goto tr5 case 6: - goto stCase6 + switch (m.data)[(m.p)] { + case 45: + goto tr9 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr9 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr9 + } + default: + goto tr9 + } + goto tr8 case 7: - goto stCase7 + switch (m.data)[(m.p)] { + case 45: + goto tr11 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr11 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr11 + } + default: + goto tr11 + } + goto tr8 case 8: - goto stCase8 + switch (m.data)[(m.p)] { + case 45: + goto tr12 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr12 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr12 + } + default: + goto tr12 + } + goto tr8 case 9: - goto stCase9 + switch (m.data)[(m.p)] { + case 45: + goto tr13 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr13 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr13 + } + default: + goto tr13 + } + goto tr8 case 10: - goto stCase10 + switch (m.data)[(m.p)] { + case 45: + goto tr14 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr14 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr14 + } + default: + goto tr14 + } + goto tr8 case 11: - goto stCase11 + switch (m.data)[(m.p)] { + case 45: + goto tr15 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr15 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr15 + } + default: + goto tr15 + } + goto tr8 case 12: - goto stCase12 + switch (m.data)[(m.p)] { + case 45: + goto tr16 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr16 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr16 + } + default: + goto tr16 + } + goto tr8 case 13: - goto stCase13 + switch (m.data)[(m.p)] { + case 45: + goto tr17 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr17 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr17 + } + default: + goto tr17 + } + goto tr8 case 14: - goto stCase14 + switch (m.data)[(m.p)] { + case 45: + goto tr18 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr18 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr18 + } + default: + goto tr18 + } + goto tr8 case 15: - goto stCase15 + switch (m.data)[(m.p)] { + case 45: + goto tr19 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr19 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr19 + } + default: + goto tr19 + } + goto tr8 case 16: - goto stCase16 + switch (m.data)[(m.p)] { + case 45: + goto tr20 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr20 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr20 + } + default: + goto tr20 + } + goto tr8 case 17: - goto stCase17 + switch (m.data)[(m.p)] { + case 45: + goto tr21 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr21 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr21 + } + default: + goto tr21 + } + goto tr8 case 18: - goto stCase18 + switch (m.data)[(m.p)] { + case 45: + goto tr22 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr22 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr22 + } + default: + goto tr22 + } + goto tr8 case 19: - goto stCase19 + switch (m.data)[(m.p)] { + case 45: + goto tr23 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr23 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr23 + } + default: + goto tr23 + } + goto tr8 case 20: - goto stCase20 + switch (m.data)[(m.p)] { + case 45: + goto tr24 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr24 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr24 + } + default: + goto tr24 + } + goto tr8 case 21: - goto stCase21 + switch (m.data)[(m.p)] { + case 45: + goto tr25 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr25 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr25 + } + default: + goto tr25 + } + goto tr8 case 22: - goto stCase22 + switch (m.data)[(m.p)] { + case 45: + goto tr26 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr26 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr26 + } + default: + goto tr26 + } + goto tr8 case 23: - goto stCase23 + switch (m.data)[(m.p)] { + case 45: + goto tr27 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr27 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr27 + } + default: + goto tr27 + } + goto tr8 case 24: - goto stCase24 + switch (m.data)[(m.p)] { + case 45: + goto tr28 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr28 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr28 + } + default: + goto tr28 + } + goto tr8 case 25: - goto stCase25 + switch (m.data)[(m.p)] { + case 45: + goto tr29 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr29 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr29 + } + default: + goto tr29 + } + goto tr8 case 26: - goto stCase26 + switch (m.data)[(m.p)] { + case 45: + goto tr30 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr30 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr30 + } + default: + goto tr30 + } + goto tr8 case 27: - goto stCase27 + switch (m.data)[(m.p)] { + case 45: + goto tr31 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr31 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr31 + } + default: + goto tr31 + } + goto tr8 case 28: - goto stCase28 + switch (m.data)[(m.p)] { + case 45: + goto tr32 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr32 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr32 + } + default: + goto tr32 + } + goto tr8 case 29: - goto stCase29 + switch (m.data)[(m.p)] { + case 45: + goto tr33 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr33 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr33 + } + default: + goto tr33 + } + goto tr8 case 30: - goto stCase30 + switch (m.data)[(m.p)] { + case 45: + goto tr34 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr34 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr34 + } + default: + goto tr34 + } + goto tr8 case 31: - goto stCase31 + switch (m.data)[(m.p)] { + case 45: + goto tr35 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr35 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr35 + } + default: + goto tr35 + } + goto tr8 case 32: - goto stCase32 + switch (m.data)[(m.p)] { + case 45: + goto tr36 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr36 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr36 + } + default: + goto tr36 + } + goto tr8 case 33: - goto stCase33 + switch (m.data)[(m.p)] { + case 45: + goto tr37 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr37 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr37 + } + default: + goto tr37 + } + goto tr8 case 34: - goto stCase34 + switch (m.data)[(m.p)] { + case 45: + goto tr38 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr38 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr38 + } + default: + goto tr38 + } + goto tr8 case 35: - goto stCase35 + switch (m.data)[(m.p)] { + case 45: + goto tr39 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr39 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr39 + } + default: + goto tr39 + } + goto tr8 case 36: - goto stCase36 + switch (m.data)[(m.p)] { + case 45: + goto tr40 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr40 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr40 + } + default: + goto tr40 + } + goto tr8 case 37: - goto stCase37 + if (m.data)[(m.p)] == 58 { + goto tr10 + } + goto tr8 case 38: - goto stCase38 - case 44: - goto stCase44 + switch (m.data)[(m.p)] { + case 33: + goto tr42 + case 36: + goto tr42 + case 37: + goto tr43 + case 61: + goto tr42 + case 95: + goto tr42 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr42 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr42 + } + case (m.data)[(m.p)] >= 64: + goto tr42 + } + default: + goto tr42 + } + goto tr41 + case 172: + switch (m.data)[(m.p)] { + case 33: + goto tr212 + case 36: + goto tr212 + case 37: + goto tr213 + case 61: + goto tr212 + case 95: + goto tr212 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr212 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr212 + } + case (m.data)[(m.p)] >= 64: + goto tr212 + } + default: + goto tr212 + } + goto tr41 case 39: - goto stCase39 + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr45 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr45 + } + default: + goto tr46 + } + goto tr44 case 40: - goto stCase40 - case 45: - goto stCase45 + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr47 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr47 + } + default: + goto tr48 + } + goto tr44 + case 173: + switch (m.data)[(m.p)] { + case 33: + goto tr212 + case 36: + goto tr212 + case 37: + goto tr213 + case 61: + goto tr212 + case 95: + goto tr212 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr212 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr212 + } + case (m.data)[(m.p)] >= 64: + goto tr212 + } + default: + goto tr212 + } + goto tr44 case 41: - goto stCase41 + switch (m.data)[(m.p)] { + case 45: + goto tr9 + case 58: + goto tr10 + case 82: + goto tr49 + case 114: + goto tr49 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr9 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr9 + } + default: + goto tr9 + } + goto tr5 case 42: - goto stCase42 + switch (m.data)[(m.p)] { + case 45: + goto tr11 + case 58: + goto tr10 + case 78: + goto tr50 + case 110: + goto tr50 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr11 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr11 + } + default: + goto tr11 + } + goto tr5 case 43: - goto stCase43 + if (m.data)[(m.p)] == 45 { + goto tr12 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr12 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr12 + } + default: + goto tr12 + } + goto tr51 + case 44: + switch (m.data)[(m.p)] { + case 85: + goto tr52 + case 117: + goto tr52 + } + goto tr0 + case 45: + switch (m.data)[(m.p)] { + case 82: + goto tr53 + case 114: + goto tr53 + } + goto tr0 case 46: - goto stCase46 - } - goto stOut - stCase1: - switch (m.data)[(m.p)] { + switch (m.data)[(m.p)] { + case 78: + goto tr54 + case 110: + goto tr54 + } + goto tr0 + case 47: + if (m.data)[(m.p)] == 58 { + goto tr55 + } + goto tr0 + case 48: + if (m.data)[(m.p)] == 105 { + goto tr57 + } + goto tr56 + case 49: + if (m.data)[(m.p)] == 101 { + goto tr58 + } + goto tr56 + case 50: + if (m.data)[(m.p)] == 116 { + goto tr59 + } + goto tr56 + case 51: + if (m.data)[(m.p)] == 102 { + goto tr60 + } + goto tr56 + case 52: + if (m.data)[(m.p)] == 58 { + goto tr61 + } + goto tr56 + case 53: + if (m.data)[(m.p)] == 112 { + goto tr62 + } + goto tr56 + case 54: + if (m.data)[(m.p)] == 97 { + goto tr63 + } + goto tr56 + case 55: + if (m.data)[(m.p)] == 114 { + goto tr64 + } + goto tr56 + case 56: + if (m.data)[(m.p)] == 97 { + goto tr65 + } + goto tr56 + case 57: + if (m.data)[(m.p)] == 109 { + goto tr66 + } + goto tr56 + case 58: + if (m.data)[(m.p)] == 115 { + goto tr67 + } + goto tr56 + case 59: + if (m.data)[(m.p)] == 58 { + goto tr68 + } + goto tr56 + case 60: + if (m.data)[(m.p)] == 115 { + goto tr69 + } + goto tr56 + case 61: + if (m.data)[(m.p)] == 99 { + goto tr70 + } + goto tr56 + case 62: + if (m.data)[(m.p)] == 105 { + goto tr71 + } + goto tr56 + case 63: + if (m.data)[(m.p)] == 109 { + goto tr72 + } + goto tr56 + case 64: + if (m.data)[(m.p)] == 58 { + goto tr73 + } + goto tr56 + case 65: + switch (m.data)[(m.p)] { + case 97: + goto tr75 + case 112: + goto tr76 + case 115: + goto tr77 + } + goto tr74 + case 66: + if (m.data)[(m.p)] == 112 { + goto tr78 + } + goto tr74 + case 67: + if (m.data)[(m.p)] == 105 { + goto tr79 + } + goto tr74 + case 68: + if (m.data)[(m.p)] == 58 { + goto tr80 + } + goto tr74 + case 69: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr82 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr82 + } + default: + goto tr82 + } + goto tr81 + case 174: + if (m.data)[(m.p)] == 58 { + goto tr215 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr214 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr214 + } + default: + goto tr214 + } + goto tr81 + case 70: + switch (m.data)[(m.p)] { + case 33: + goto tr84 + case 36: + goto tr84 + case 37: + goto tr85 + case 61: + goto tr84 + case 95: + goto tr84 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr84 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr84 + } + case (m.data)[(m.p)] >= 64: + goto tr84 + } + default: + goto tr84 + } + goto tr83 + case 175: + switch (m.data)[(m.p)] { + case 33: + goto tr216 + case 36: + goto tr216 + case 37: + goto tr217 + case 61: + goto tr216 + case 95: + goto tr216 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr216 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr216 + } + case (m.data)[(m.p)] >= 64: + goto tr216 + } + default: + goto tr216 + } + goto tr83 + case 71: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr87 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr87 + } + default: + goto tr88 + } + goto tr86 + case 72: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr89 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr89 + } + default: + goto tr90 + } + goto tr86 + case 176: + switch (m.data)[(m.p)] { + case 33: + goto tr216 + case 36: + goto tr216 + case 37: + goto tr217 + case 61: + goto tr216 + case 95: + goto tr216 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr216 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr216 + } + case (m.data)[(m.p)] >= 64: + goto tr216 + } + default: + goto tr216 + } + goto tr86 + case 73: + if (m.data)[(m.p)] == 97 { + goto tr91 + } + goto tr74 + case 74: + if (m.data)[(m.p)] == 114 { + goto tr92 + } + goto tr74 + case 75: + if (m.data)[(m.p)] == 97 { + goto tr93 + } + goto tr74 + case 76: + if (m.data)[(m.p)] == 109 { + goto tr79 + } + goto tr74 + case 77: + if (m.data)[(m.p)] == 99 { + goto tr94 + } + goto tr74 + case 78: + if (m.data)[(m.p)] == 104 { + goto tr95 + } + goto tr74 + case 79: + if (m.data)[(m.p)] == 101 { + goto tr96 + } + goto tr74 + case 80: + if (m.data)[(m.p)] == 109 { + goto tr97 + } + goto tr74 + case 81: + if (m.data)[(m.p)] == 97 { + goto tr98 + } + goto tr74 + case 82: + if (m.data)[(m.p)] == 115 { + goto tr79 + } + goto tr74 + case 83: + switch (m.data)[(m.p)] { + case 85: + goto tr99 + case 117: + goto tr99 + } + goto tr0 + case 84: + switch (m.data)[(m.p)] { + case 82: + goto tr100 + case 114: + goto tr100 + } + goto tr0 case 85: - goto tr1 + switch (m.data)[(m.p)] { + case 78: + goto tr101 + case 110: + goto tr101 + } + goto tr0 + case 86: + if (m.data)[(m.p)] == 58 { + goto tr102 + } + goto tr0 + case 87: + switch (m.data)[(m.p)] { + case 85: + goto tr105 + case 117: + goto tr105 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr104 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr104 + } + default: + goto tr104 + } + goto tr103 + case 88: + if (m.data)[(m.p)] == 45 { + goto tr107 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr108 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr108 + } + default: + goto tr108 + } + goto tr106 + case 89: + if (m.data)[(m.p)] == 45 { + goto tr109 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr110 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr110 + } + default: + goto tr110 + } + goto tr106 + case 90: + if (m.data)[(m.p)] == 45 { + goto tr111 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr112 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr112 + } + default: + goto tr112 + } + goto tr106 + case 91: + if (m.data)[(m.p)] == 45 { + goto tr113 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr114 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr114 + } + default: + goto tr114 + } + goto tr106 + case 92: + if (m.data)[(m.p)] == 45 { + goto tr115 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr116 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr116 + } + default: + goto tr116 + } + goto tr106 + case 93: + if (m.data)[(m.p)] == 45 { + goto tr117 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr118 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr118 + } + default: + goto tr118 + } + goto tr106 + case 94: + if (m.data)[(m.p)] == 45 { + goto tr119 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr120 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr120 + } + default: + goto tr120 + } + goto tr106 + case 95: + if (m.data)[(m.p)] == 45 { + goto tr121 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr122 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr122 + } + default: + goto tr122 + } + goto tr106 + case 96: + if (m.data)[(m.p)] == 45 { + goto tr123 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr124 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr124 + } + default: + goto tr124 + } + goto tr106 + case 97: + if (m.data)[(m.p)] == 45 { + goto tr125 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr126 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr126 + } + default: + goto tr126 + } + goto tr106 + case 98: + if (m.data)[(m.p)] == 45 { + goto tr127 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr128 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr128 + } + default: + goto tr128 + } + goto tr106 + case 99: + if (m.data)[(m.p)] == 45 { + goto tr129 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr130 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr130 + } + default: + goto tr130 + } + goto tr106 + case 100: + if (m.data)[(m.p)] == 45 { + goto tr131 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr132 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr132 + } + default: + goto tr132 + } + goto tr106 + case 101: + if (m.data)[(m.p)] == 45 { + goto tr133 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr134 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr134 + } + default: + goto tr134 + } + goto tr106 + case 102: + if (m.data)[(m.p)] == 45 { + goto tr135 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr136 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr136 + } + default: + goto tr136 + } + goto tr106 + case 103: + if (m.data)[(m.p)] == 45 { + goto tr137 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr138 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr138 + } + default: + goto tr138 + } + goto tr106 + case 104: + if (m.data)[(m.p)] == 45 { + goto tr139 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr140 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr140 + } + default: + goto tr140 + } + goto tr106 + case 105: + if (m.data)[(m.p)] == 45 { + goto tr141 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr142 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr142 + } + default: + goto tr142 + } + goto tr106 + case 106: + if (m.data)[(m.p)] == 45 { + goto tr143 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr144 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr144 + } + default: + goto tr144 + } + goto tr106 + case 107: + if (m.data)[(m.p)] == 45 { + goto tr145 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr146 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr146 + } + default: + goto tr146 + } + goto tr106 + case 108: + if (m.data)[(m.p)] == 45 { + goto tr147 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr148 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr148 + } + default: + goto tr148 + } + goto tr106 + case 109: + if (m.data)[(m.p)] == 45 { + goto tr149 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr150 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr150 + } + default: + goto tr150 + } + goto tr106 + case 110: + if (m.data)[(m.p)] == 45 { + goto tr151 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr152 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr152 + } + default: + goto tr152 + } + goto tr106 + case 111: + if (m.data)[(m.p)] == 45 { + goto tr153 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr154 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr154 + } + default: + goto tr154 + } + goto tr106 + case 112: + if (m.data)[(m.p)] == 45 { + goto tr155 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr156 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr156 + } + default: + goto tr156 + } + goto tr106 + case 113: + if (m.data)[(m.p)] == 45 { + goto tr157 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr158 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr158 + } + default: + goto tr158 + } + goto tr106 + case 114: + if (m.data)[(m.p)] == 45 { + goto tr159 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr160 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr160 + } + default: + goto tr160 + } + goto tr106 + case 115: + if (m.data)[(m.p)] == 45 { + goto tr161 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr162 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr162 + } + default: + goto tr162 + } + goto tr106 + case 116: + if (m.data)[(m.p)] == 45 { + goto tr163 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr164 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr164 + } + default: + goto tr164 + } + goto tr106 case 117: - goto tr1 + if (m.data)[(m.p)] == 45 { + goto tr165 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr166 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr166 + } + default: + goto tr166 + } + goto tr106 + case 118: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr167 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr167 + } + default: + goto tr167 + } + goto tr106 + case 119: + if (m.data)[(m.p)] == 58 { + goto tr168 + } + goto tr106 + case 120: + switch (m.data)[(m.p)] { + case 33: + goto tr170 + case 37: + goto tr171 + case 61: + goto tr170 + case 95: + goto tr170 + case 126: + goto tr170 + } + switch { + case (m.data)[(m.p)] < 48: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr170 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr170 + } + case (m.data)[(m.p)] >= 64: + goto tr170 + } + default: + goto tr170 + } + goto tr169 + case 177: + switch (m.data)[(m.p)] { + case 33: + goto tr218 + case 35: + goto tr219 + case 37: + goto tr220 + case 61: + goto tr218 + case 63: + goto tr221 + case 95: + goto tr218 + case 126: + goto tr218 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr218 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr218 + } + default: + goto tr218 + } + goto tr169 + case 178: + switch (m.data)[(m.p)] { + case 33: + goto tr222 + case 37: + goto tr223 + case 61: + goto tr222 + case 95: + goto tr222 + case 126: + goto tr222 + } + switch { + case (m.data)[(m.p)] < 63: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr222 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr222 + } + default: + goto tr222 + } + goto tr183 + case 179: + switch (m.data)[(m.p)] { + case 33: + goto tr224 + case 37: + goto tr225 + case 61: + goto tr224 + case 95: + goto tr224 + case 126: + goto tr224 + } + switch { + case (m.data)[(m.p)] < 63: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr224 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr224 + } + default: + goto tr224 + } + goto tr183 + case 121: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr173 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr173 + } + default: + goto tr174 + } + goto tr172 + case 122: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr175 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr175 + } + default: + goto tr176 + } + goto tr172 + case 180: + switch (m.data)[(m.p)] { + case 33: + goto tr224 + case 37: + goto tr225 + case 61: + goto tr224 + case 95: + goto tr224 + case 126: + goto tr224 + } + switch { + case (m.data)[(m.p)] < 63: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr224 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr224 + } + default: + goto tr224 + } + goto tr172 + case 123: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr178 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr178 + } + default: + goto tr179 + } + goto tr177 + case 124: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr180 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr180 + } + default: + goto tr181 + } + goto tr177 + case 181: + switch (m.data)[(m.p)] { + case 33: + goto tr218 + case 35: + goto tr219 + case 37: + goto tr220 + case 61: + goto tr218 + case 63: + goto tr221 + case 95: + goto tr218 + case 126: + goto tr218 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr218 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr218 + } + default: + goto tr218 + } + goto tr177 + case 125: + switch (m.data)[(m.p)] { + case 43: + goto tr182 + case 61: + goto tr184 + } + goto tr183 + case 126: + switch (m.data)[(m.p)] { + case 33: + goto tr186 + case 37: + goto tr187 + case 61: + goto tr186 + case 63: + goto tr188 + case 95: + goto tr186 + case 126: + goto tr186 + } + switch { + case (m.data)[(m.p)] < 48: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr186 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr186 + } + case (m.data)[(m.p)] >= 64: + goto tr186 + } + default: + goto tr186 + } + goto tr185 + case 182: + switch (m.data)[(m.p)] { + case 33: + goto tr226 + case 35: + goto tr227 + case 37: + goto tr228 + case 61: + goto tr226 + case 63: + goto tr229 + case 95: + goto tr226 + case 126: + goto tr226 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr226 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr226 + } + default: + goto tr226 + } + goto tr185 + case 127: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr190 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr190 + } + default: + goto tr191 + } + goto tr189 + case 128: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr192 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr192 + } + default: + goto tr193 + } + goto tr189 + case 183: + switch (m.data)[(m.p)] { + case 33: + goto tr226 + case 35: + goto tr227 + case 37: + goto tr228 + case 61: + goto tr226 + case 63: + goto tr229 + case 95: + goto tr226 + case 126: + goto tr226 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr226 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr226 + } + default: + goto tr226 + } + goto tr189 + case 184: + switch (m.data)[(m.p)] { + case 33: + goto tr226 + case 35: + goto tr227 + case 37: + goto tr228 + case 43: + goto tr230 + case 61: + goto tr231 + case 63: + goto tr229 + case 95: + goto tr226 + case 126: + goto tr226 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr226 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr226 + } + default: + goto tr226 + } + goto tr185 + case 185: + switch (m.data)[(m.p)] { + case 33: + goto tr232 + case 35: + goto tr233 + case 37: + goto tr234 + case 47: + goto tr226 + case 61: + goto tr232 + case 63: + goto tr235 + case 95: + goto tr232 + case 126: + goto tr232 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr232 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr232 + } + default: + goto tr232 + } + goto tr185 + case 186: + switch (m.data)[(m.p)] { + case 33: + goto tr204 + case 35: + goto tr227 + case 37: + goto tr237 + case 47: + goto tr226 + case 61: + goto tr204 + case 63: + goto tr229 + case 95: + goto tr204 + case 126: + goto tr204 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr204 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr204 + } + default: + goto tr204 + } + goto tr236 + case 187: + switch (m.data)[(m.p)] { + case 33: + goto tr238 + case 35: + goto tr239 + case 37: + goto tr240 + case 61: + goto tr238 + case 63: + goto tr241 + case 95: + goto tr238 + case 126: + goto tr238 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr238 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr238 + } + default: + goto tr238 + } + goto tr203 + case 129: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr195 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr195 + } + default: + goto tr196 + } + goto tr194 + case 130: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr197 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr197 + } + default: + goto tr198 + } + goto tr194 + case 188: + switch (m.data)[(m.p)] { + case 33: + goto tr238 + case 35: + goto tr239 + case 37: + goto tr240 + case 61: + goto tr238 + case 63: + goto tr241 + case 95: + goto tr238 + case 126: + goto tr238 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr238 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr238 + } + default: + goto tr238 + } + goto tr194 + case 189: + switch (m.data)[(m.p)] { + case 33: + goto tr238 + case 35: + goto tr239 + case 37: + goto tr240 + case 61: + goto tr242 + case 63: + goto tr241 + case 95: + goto tr238 + case 126: + goto tr238 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr238 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr238 + } + default: + goto tr238 + } + goto tr203 + case 190: + switch (m.data)[(m.p)] { + case 33: + goto tr243 + case 35: + goto tr244 + case 37: + goto tr245 + case 47: + goto tr238 + case 61: + goto tr243 + case 63: + goto tr246 + case 95: + goto tr243 + case 126: + goto tr243 + } + switch { + case (m.data)[(m.p)] < 64: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 59 { + goto tr243 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr243 + } + default: + goto tr243 + } + goto tr203 + case 131: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr200 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr200 + } + default: + goto tr201 + } + goto tr199 + case 132: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr197 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr197 + } + default: + goto tr198 + } + goto tr199 + case 133: + if (m.data)[(m.p)] == 43 { + goto tr202 + } + goto tr185 + case 191: + switch (m.data)[(m.p)] { + case 33: + goto tr232 + case 35: + goto tr233 + case 37: + goto tr234 + case 61: + goto tr232 + case 63: + goto tr247 + case 95: + goto tr232 + case 126: + goto tr232 + } + switch { + case (m.data)[(m.p)] < 48: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr232 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr232 + } + case (m.data)[(m.p)] >= 64: + goto tr232 + } + default: + goto tr232 + } + goto tr185 + case 134: + switch (m.data)[(m.p)] { + case 43: + goto tr202 + case 61: + goto tr184 + } + goto tr185 + case 135: + switch (m.data)[(m.p)] { + case 33: + goto tr204 + case 37: + goto tr205 + case 61: + goto tr204 + case 63: + goto tr206 + case 95: + goto tr204 + case 126: + goto tr204 + } + switch { + case (m.data)[(m.p)] < 48: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr204 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr204 + } + case (m.data)[(m.p)] >= 64: + goto tr204 + } + default: + goto tr204 + } + goto tr203 + case 136: + if (m.data)[(m.p)] == 61 { + goto tr207 + } + goto tr203 + case 192: + switch (m.data)[(m.p)] { + case 33: + goto tr243 + case 35: + goto tr244 + case 37: + goto tr245 + case 61: + goto tr243 + case 63: + goto tr248 + case 95: + goto tr243 + case 126: + goto tr243 + } + switch { + case (m.data)[(m.p)] < 48: + if 36 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr243 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr243 + } + case (m.data)[(m.p)] >= 64: + goto tr243 + } + default: + goto tr243 + } + goto tr203 + case 137: + if (m.data)[(m.p)] == 58 { + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr167 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr167 + } + default: + goto tr167 + } + goto tr106 + case 138: + switch (m.data)[(m.p)] { + case 45: + goto tr165 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr166 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr166 + } + default: + goto tr166 + } + goto tr106 + case 139: + switch (m.data)[(m.p)] { + case 45: + goto tr163 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr164 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr164 + } + default: + goto tr164 + } + goto tr106 + case 140: + switch (m.data)[(m.p)] { + case 45: + goto tr161 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr162 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr162 + } + default: + goto tr162 + } + goto tr106 + case 141: + switch (m.data)[(m.p)] { + case 45: + goto tr159 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr160 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr160 + } + default: + goto tr160 + } + goto tr106 + case 142: + switch (m.data)[(m.p)] { + case 45: + goto tr157 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr158 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr158 + } + default: + goto tr158 + } + goto tr106 + case 143: + switch (m.data)[(m.p)] { + case 45: + goto tr155 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr156 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr156 + } + default: + goto tr156 + } + goto tr106 + case 144: + switch (m.data)[(m.p)] { + case 45: + goto tr153 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr154 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr154 + } + default: + goto tr154 + } + goto tr106 + case 145: + switch (m.data)[(m.p)] { + case 45: + goto tr151 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr152 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr152 + } + default: + goto tr152 + } + goto tr106 + case 146: + switch (m.data)[(m.p)] { + case 45: + goto tr149 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr150 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr150 + } + default: + goto tr150 + } + goto tr106 + case 147: + switch (m.data)[(m.p)] { + case 45: + goto tr147 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr148 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr148 + } + default: + goto tr148 + } + goto tr106 + case 148: + switch (m.data)[(m.p)] { + case 45: + goto tr145 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr146 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr146 + } + default: + goto tr146 + } + goto tr106 + case 149: + switch (m.data)[(m.p)] { + case 45: + goto tr143 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr144 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr144 + } + default: + goto tr144 + } + goto tr106 + case 150: + switch (m.data)[(m.p)] { + case 45: + goto tr141 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr142 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr142 + } + default: + goto tr142 + } + goto tr106 + case 151: + switch (m.data)[(m.p)] { + case 45: + goto tr139 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr140 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr140 + } + default: + goto tr140 + } + goto tr106 + case 152: + switch (m.data)[(m.p)] { + case 45: + goto tr137 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr138 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr138 + } + default: + goto tr138 + } + goto tr106 + case 153: + switch (m.data)[(m.p)] { + case 45: + goto tr135 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr136 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr136 + } + default: + goto tr136 + } + goto tr106 + case 154: + switch (m.data)[(m.p)] { + case 45: + goto tr133 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr134 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr134 + } + default: + goto tr134 + } + goto tr106 + case 155: + switch (m.data)[(m.p)] { + case 45: + goto tr131 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr132 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr132 + } + default: + goto tr132 + } + goto tr106 + case 156: + switch (m.data)[(m.p)] { + case 45: + goto tr129 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr130 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr130 + } + default: + goto tr130 + } + goto tr106 + case 157: + switch (m.data)[(m.p)] { + case 45: + goto tr127 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr128 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr128 + } + default: + goto tr128 + } + goto tr106 + case 158: + switch (m.data)[(m.p)] { + case 45: + goto tr125 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr126 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr126 + } + default: + goto tr126 + } + goto tr106 + case 159: + switch (m.data)[(m.p)] { + case 45: + goto tr123 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr124 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr124 + } + default: + goto tr124 + } + goto tr106 + case 160: + switch (m.data)[(m.p)] { + case 45: + goto tr121 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr122 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr122 + } + default: + goto tr122 + } + goto tr106 + case 161: + switch (m.data)[(m.p)] { + case 45: + goto tr119 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr120 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr120 + } + default: + goto tr120 + } + goto tr106 + case 162: + switch (m.data)[(m.p)] { + case 45: + goto tr117 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr118 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr118 + } + default: + goto tr118 + } + goto tr106 + case 163: + switch (m.data)[(m.p)] { + case 45: + goto tr115 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr116 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr116 + } + default: + goto tr116 + } + goto tr106 + case 164: + switch (m.data)[(m.p)] { + case 45: + goto tr113 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr114 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr114 + } + default: + goto tr114 + } + goto tr106 + case 165: + switch (m.data)[(m.p)] { + case 45: + goto tr111 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr112 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr112 + } + default: + goto tr112 + } + goto tr106 + case 166: + switch (m.data)[(m.p)] { + case 45: + goto tr109 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr110 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr110 + } + default: + goto tr110 + } + goto tr106 + case 167: + switch (m.data)[(m.p)] { + case 45: + goto tr107 + case 82: + goto tr208 + case 114: + goto tr208 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr108 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr108 + } + default: + goto tr108 + } + goto tr103 + case 168: + switch (m.data)[(m.p)] { + case 45: + goto tr109 + case 58: + goto tr168 + case 78: + goto tr209 + case 110: + goto tr209 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr110 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr110 + } + default: + goto tr110 + } + goto tr103 + case 169: + switch (m.data)[(m.p)] { + case 45: + goto tr210 + case 58: + goto tr168 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr112 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr112 + } + default: + goto tr112 + } + goto tr106 + case 170: + switch (m.data)[(m.p)] { + case 45: + goto tr113 + case 48: + goto tr211 + } + switch { + case (m.data)[(m.p)] < 65: + if 49 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr114 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr211 + } + default: + goto tr211 + } + goto tr106 + case 171: + if (m.data)[(m.p)] == 45 { + goto tr115 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr116 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr116 + } + default: + goto tr116 + } + goto tr106 + case 193: + switch (m.data)[(m.p)] { + case 10: + goto tr183 + case 13: + goto tr183 + } + goto tr249 } - goto tr0 + + tr183: + m.cs = 0 + goto _again tr0: - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr3: - - m.err = fmt.Errorf(errPrefix, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr6: - - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 + m.cs = 0 + goto f0 + tr5: + m.cs = 0 + goto f3 + tr8: + m.cs = 0 + goto f5 tr41: - - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 + m.cs = 0 + goto f7 tr44: - - m.err = fmt.Errorf(errHex, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 + m.cs = 0 + goto f8 + tr51: + m.cs = 0 + goto f10 + tr56: + m.cs = 0 + goto f11 + tr74: + m.cs = 0 + goto f13 + tr81: + m.cs = 0 + goto f15 + tr83: + m.cs = 0 + goto f17 + tr86: + m.cs = 0 + goto f19 + tr103: + m.cs = 0 + goto f20 + tr106: + m.cs = 0 + goto f21 + tr169: + m.cs = 0 + goto f22 + tr172: + m.cs = 0 + goto f23 + tr177: + m.cs = 0 + goto f24 + tr185: + m.cs = 0 + goto f25 + tr189: + m.cs = 0 + goto f27 + tr194: + m.cs = 0 + goto f28 + tr199: + m.cs = 0 + goto f29 + tr203: + m.cs = 0 + goto f30 + tr236: + m.cs = 0 + goto f46 + tr1: + m.cs = 2 + goto f1 + tr2: + m.cs = 3 + goto _again + tr3: + m.cs = 4 + goto _again + tr4: + m.cs = 5 + goto f2 + tr6: + m.cs = 6 + goto f4 + tr9: + m.cs = 7 + goto _again + tr11: + m.cs = 8 + goto _again + tr12: + m.cs = 9 + goto _again + tr13: + m.cs = 10 + goto _again + tr14: + m.cs = 11 + goto _again + tr15: + m.cs = 12 + goto _again + tr16: + m.cs = 13 + goto _again + tr17: + m.cs = 14 + goto _again + tr18: + m.cs = 15 + goto _again + tr19: + m.cs = 16 + goto _again + tr20: + m.cs = 17 + goto _again + tr21: + m.cs = 18 + goto _again + tr22: + m.cs = 19 + goto _again + tr23: + m.cs = 20 + goto _again + tr24: + m.cs = 21 + goto _again + tr25: + m.cs = 22 + goto _again + tr26: + m.cs = 23 + goto _again + tr27: + m.cs = 24 + goto _again + tr28: + m.cs = 25 + goto _again + tr29: + m.cs = 26 + goto _again + tr30: + m.cs = 27 + goto _again + tr31: + m.cs = 28 + goto _again + tr32: + m.cs = 29 + goto _again + tr33: + m.cs = 30 + goto _again + tr34: + m.cs = 31 + goto _again + tr35: + m.cs = 32 + goto _again + tr36: + m.cs = 33 + goto _again + tr37: + m.cs = 34 + goto _again + tr38: + m.cs = 35 + goto _again + tr39: + m.cs = 36 + goto _again + tr40: + m.cs = 37 + goto _again + tr10: + m.cs = 38 + goto f6 + tr213: + m.cs = 39 + goto _again + tr43: + m.cs = 39 + goto f4 + tr45: + m.cs = 40 + goto _again + tr46: + m.cs = 40 + goto f9 + tr7: + m.cs = 41 + goto f1 + tr49: + m.cs = 42 + goto _again tr50: + m.cs = 43 + goto _again + tr52: + m.cs = 45 + goto f1 + tr53: + m.cs = 46 + goto _again + tr54: + m.cs = 47 + goto _again + tr55: + m.cs = 48 + goto f2 + tr57: + m.cs = 49 + goto f4 + tr58: + m.cs = 50 + goto _again + tr59: + m.cs = 51 + goto _again + tr60: + m.cs = 52 + goto _again + tr61: + m.cs = 53 + goto _again + tr62: + m.cs = 54 + goto _again + tr63: + m.cs = 55 + goto _again + tr64: + m.cs = 56 + goto _again + tr65: + m.cs = 57 + goto _again + tr66: + m.cs = 58 + goto _again + tr67: + m.cs = 59 + goto _again + tr68: + m.cs = 60 + goto _again + tr69: + m.cs = 61 + goto _again + tr70: + m.cs = 62 + goto _again + tr71: + m.cs = 63 + goto _again + tr72: + m.cs = 64 + goto _again + tr73: + m.cs = 65 + goto f12 + tr75: + m.cs = 66 + goto f4 + tr78: + m.cs = 67 + goto _again + tr79: + m.cs = 68 + goto _again + tr80: + m.cs = 69 + goto f14 + tr215: + m.cs = 70 + goto f35 + tr217: + m.cs = 71 + goto _again + tr85: + m.cs = 71 + goto f18 + tr87: + m.cs = 72 + goto _again + tr88: + m.cs = 72 + goto f9 + tr76: + m.cs = 73 + goto f4 + tr91: + m.cs = 74 + goto _again + tr92: + m.cs = 75 + goto _again + tr93: + m.cs = 76 + goto _again + tr77: + m.cs = 77 + goto f4 + tr94: + m.cs = 78 + goto _again + tr95: + m.cs = 79 + goto _again + tr96: + m.cs = 80 + goto _again + tr97: + m.cs = 81 + goto _again + tr98: + m.cs = 82 + goto _again + tr99: + m.cs = 84 + goto f1 + tr100: + m.cs = 85 + goto _again + tr101: + m.cs = 86 + goto _again + tr102: + m.cs = 87 + goto f2 + tr104: + m.cs = 88 + goto f4 + tr107: + m.cs = 89 + goto _again + tr109: + m.cs = 90 + goto _again + tr111: + m.cs = 91 + goto _again + tr113: + m.cs = 92 + goto _again + tr115: + m.cs = 93 + goto _again + tr117: + m.cs = 94 + goto _again + tr119: + m.cs = 95 + goto _again + tr121: + m.cs = 96 + goto _again + tr123: + m.cs = 97 + goto _again + tr125: + m.cs = 98 + goto _again + tr127: + m.cs = 99 + goto _again + tr129: + m.cs = 100 + goto _again + tr131: + m.cs = 101 + goto _again + tr133: + m.cs = 102 + goto _again + tr135: + m.cs = 103 + goto _again + tr137: + m.cs = 104 + goto _again + tr139: + m.cs = 105 + goto _again + tr141: + m.cs = 106 + goto _again + tr143: + m.cs = 107 + goto _again + tr145: + m.cs = 108 + goto _again + tr147: + m.cs = 109 + goto _again + tr149: + m.cs = 110 + goto _again + tr151: + m.cs = 111 + goto _again + tr153: + m.cs = 112 + goto _again + tr155: + m.cs = 113 + goto _again + tr157: + m.cs = 114 + goto _again + tr159: + m.cs = 115 + goto _again + tr161: + m.cs = 116 + goto _again + tr163: + m.cs = 117 + goto _again + tr165: + m.cs = 118 + goto _again + tr167: + m.cs = 119 + goto _again + tr168: + m.cs = 120 + goto f6 + tr225: + m.cs = 121 + goto _again + tr223: + m.cs = 121 + goto f4 + tr173: + m.cs = 122 + goto _again + tr174: + m.cs = 122 + goto f9 + tr220: + m.cs = 123 + goto _again + tr171: + m.cs = 123 + goto f4 + tr178: + m.cs = 124 + goto _again + tr179: + m.cs = 124 + goto f9 + tr221: + m.cs = 125 + goto f38 + tr182: + m.cs = 126 + goto _again + tr228: + m.cs = 127 + goto _again + tr187: + m.cs = 127 + goto f26 + tr234: + m.cs = 127 + goto f44 + tr190: + m.cs = 128 + goto _again + tr191: + m.cs = 128 + goto f9 + tr240: + m.cs = 129 + goto _again + tr205: + m.cs = 129 + goto f31 + tr245: + m.cs = 129 + goto f50 + tr195: + m.cs = 130 + goto _again + tr196: + m.cs = 130 + goto f9 + tr237: + m.cs = 131 + goto f31 + tr200: + m.cs = 132 + goto _again + tr201: + m.cs = 132 + goto f9 + tr188: + m.cs = 133 + goto f26 + tr247: + m.cs = 134 + goto f45 + tr184: + m.cs = 135 + goto _again + tr206: + m.cs = 136 + goto f31 + tr248: + m.cs = 136 + goto f50 + tr166: + m.cs = 137 + goto _again + tr164: + m.cs = 138 + goto _again + tr162: + m.cs = 139 + goto _again + tr160: + m.cs = 140 + goto _again + tr158: + m.cs = 141 + goto _again + tr156: + m.cs = 142 + goto _again + tr154: + m.cs = 143 + goto _again + tr152: + m.cs = 144 + goto _again + tr150: + m.cs = 145 + goto _again + tr148: + m.cs = 146 + goto _again + tr146: + m.cs = 147 + goto _again + tr144: + m.cs = 148 + goto _again + tr142: + m.cs = 149 + goto _again + tr140: + m.cs = 150 + goto _again + tr138: + m.cs = 151 + goto _again + tr136: + m.cs = 152 + goto _again + tr134: + m.cs = 153 + goto _again + tr132: + m.cs = 154 + goto _again + tr130: + m.cs = 155 + goto _again + tr128: + m.cs = 156 + goto _again + tr126: + m.cs = 157 + goto _again + tr124: + m.cs = 158 + goto _again + tr122: + m.cs = 159 + goto _again + tr120: + m.cs = 160 + goto _again + tr118: + m.cs = 161 + goto _again + tr116: + m.cs = 162 + goto _again + tr114: + m.cs = 163 + goto _again + tr112: + m.cs = 164 + goto _again + tr110: + m.cs = 165 + goto _again + tr108: + m.cs = 166 + goto _again + tr105: + m.cs = 167 + goto f1 + tr208: + m.cs = 168 + goto _again + tr209: + m.cs = 169 + goto _again + tr210: + m.cs = 170 + goto f2 + tr211: + m.cs = 171 + goto _again + tr212: + m.cs = 172 + goto _again + tr42: + m.cs = 172 + goto f4 + tr47: + m.cs = 173 + goto _again + tr48: + m.cs = 173 + goto f9 + tr214: + m.cs = 174 + goto _again + tr82: + m.cs = 174 + goto f16 + tr216: + m.cs = 175 + goto _again + tr84: + m.cs = 175 + goto f18 + tr89: + m.cs = 176 + goto _again + tr90: + m.cs = 176 + goto f9 + tr218: + m.cs = 177 + goto _again + tr170: + m.cs = 177 + goto f4 + tr219: + m.cs = 178 + goto f38 + tr227: + m.cs = 178 + goto f42 + tr233: + m.cs = 178 + goto f45 + tr239: + m.cs = 178 + goto f48 + tr244: + m.cs = 178 + goto f51 + tr224: + m.cs = 179 + goto _again + tr222: + m.cs = 179 + goto f4 + tr175: + m.cs = 180 + goto _again + tr176: + m.cs = 180 + goto f9 + tr180: + m.cs = 181 + goto _again + tr181: + m.cs = 181 + goto f9 + tr226: + m.cs = 182 + goto _again + tr186: + m.cs = 182 + goto f26 + tr232: + m.cs = 182 + goto f44 + tr192: + m.cs = 183 + goto _again + tr193: + m.cs = 183 + goto f9 + tr229: + m.cs = 184 + goto f42 + tr235: + m.cs = 184 + goto f45 + tr230: + m.cs = 185 + goto _again + tr231: + m.cs = 186 + goto _again + tr238: + m.cs = 187 + goto _again + tr204: + m.cs = 187 + goto f31 + tr243: + m.cs = 187 + goto f50 + tr197: + m.cs = 188 + goto _again + tr198: + m.cs = 188 + goto f9 + tr241: + m.cs = 189 + goto _again + tr246: + m.cs = 189 + goto f50 + tr242: + m.cs = 190 + goto _again + tr202: + m.cs = 191 + goto _again + tr207: + m.cs = 192 + goto _again + tr249: + m.cs = 193 + goto _again + + f4: + + m.pb = m.p + + goto _again + f9: + + // List of positions in the buffer to later lowercase + output.tolower = append(output.tolower, m.p-m.pb) + + goto _again + f2: + + output.prefix = string(m.text()) + + goto _again + f6: + + output.ID = string(m.text()) + + goto _again + f38: + + output.SS = string(m.text()) + // Iterate upper letters lowering them + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 + } + + goto _again + f0: m.err = fmt.Errorf(errPrefix, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again + + goto _again + f5: m.err = fmt.Errorf(errIdentifier, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - m.err = fmt.Errorf(errParse, m.p) + goto _again + f7: + + m.err = fmt.Errorf(errSpecificString, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + goto _again + f23: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - goto st0 - tr52: + goto _again + f11: + + m.err = fmt.Errorf(errSCIMNamespace, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f13: + + m.err = fmt.Errorf(errSCIMType, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f15: + + m.err = fmt.Errorf(errSCIMName, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f17: + + if m.p == m.pe { + m.err = fmt.Errorf(errSCIMOtherIncomplete, m.p-1) + } else { + m.err = fmt.Errorf(errSCIMOther, m.p) + } + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f14: + + output.scim.Type = scimschema.TypeFromString(string(m.text())) + + goto _again + f16: + + output.scim.pos = m.p + + goto _again + f35: + + output.scim.Name = string(m.data[output.scim.pos:m.p]) + + goto _again + f18: + + output.scim.pos = m.p + + goto _again + f22: + + m.err = fmt.Errorf(err8141SpecificString, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f21: + + m.err = fmt.Errorf(err8141Identifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f42: + + output.rComponent = string(m.text()) + + goto _again + f48: + + output.qComponent = string(m.text()) + + goto _again + f44: + + if output.rStart { + m.err = fmt.Errorf(err8141RComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + output.rStart = true + + goto _again + f50: + + if output.qStart { + m.err = fmt.Errorf(err8141QComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + output.qStart = true + + goto _again + f25: + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f30: + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f1: + + m.pb = m.p + + if m.parsingMode != RFC8141Only { + // Throw an error when: + // - we are entering here matching the the prefix in the namespace identifier part + // - looking ahead (3 chars) we find a colon + if pos := m.p + 3; pos < m.pe && m.data[pos] == 58 && output.prefix != "" { + m.err = fmt.Errorf(errNoUrnWithinID, pos) + (m.p)-- + + m.cs = 193 + goto _again + + } + } + + goto _again + f12: + + output.ID = string(m.text()) + + output.scim = &SCIM{} + + goto _again + f3: + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f10: + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again m.err = fmt.Errorf(errNoUrnWithinID, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + goto _again + f8: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - m.err = fmt.Errorf(errIdentifier, m.p) + m.err = fmt.Errorf(errSpecificString, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + goto _again + f19: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - m.err = fmt.Errorf(errParse, m.p) + if m.p == m.pe { + m.err = fmt.Errorf(errSCIMOtherIncomplete, m.p-1) + } else { + m.err = fmt.Errorf(errSCIMOther, m.p) + } (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + goto _again + f24: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - goto st0 - stCase0: - st0: - m.cs = 0 - goto _out - tr1: + m.err = fmt.Errorf(err8141SpecificString, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f27: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f28: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f20: + + m.err = fmt.Errorf(err8141Identifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f26: + + if output.rStart { + m.err = fmt.Errorf(err8141RComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + output.rStart = true m.pb = m.p - goto st2 - st2: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof2 - } - stCase2: - switch (m.data)[(m.p)] { - case 82: - goto st3 - case 114: - goto st3 - } - goto tr0 - st3: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof3 - } - stCase3: - switch (m.data)[(m.p)] { - case 78: - goto st4 - case 110: - goto st4 - } - goto tr3 - st4: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof4 - } - stCase4: - if (m.data)[(m.p)] == 58 { - goto tr5 - } - goto tr0 - tr5: + goto _again + f45: - output.prefix = string(m.text()) + if output.rStart { + m.err = fmt.Errorf(err8141RComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again - goto st5 - st5: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof5 } - stCase5: - switch (m.data)[(m.p)] { - case 85: - goto tr8 - case 117: - goto tr8 + output.rStart = true + + output.rComponent = string(m.text()) + + goto _again + f31: + + if output.qStart { + m.err = fmt.Errorf(err8141QComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto tr7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto tr7 - } - default: - goto tr7 - } - goto tr6 - tr7: + output.qStart = true m.pb = m.p - goto st6 - st6: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof6 - } - stCase6: - switch (m.data)[(m.p)] { - case 45: - goto st7 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st7 - } - default: - goto st7 - } - goto tr6 - st7: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof7 - } - stCase7: - switch (m.data)[(m.p)] { - case 45: - goto st8 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st8 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st8 - } - default: - goto st8 - } - goto tr6 - st8: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof8 - } - stCase8: - switch (m.data)[(m.p)] { - case 45: - goto st9 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st9 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st9 - } - default: - goto st9 - } - goto tr6 - st9: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof9 - } - stCase9: - switch (m.data)[(m.p)] { - case 45: - goto st10 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st10 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st10 - } - default: - goto st10 - } - goto tr6 - st10: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof10 - } - stCase10: - switch (m.data)[(m.p)] { - case 45: - goto st11 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st11 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st11 - } - default: - goto st11 - } - goto tr6 - st11: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof11 - } - stCase11: - switch (m.data)[(m.p)] { - case 45: - goto st12 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st12 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st12 - } - default: - goto st12 - } - goto tr6 - st12: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof12 - } - stCase12: - switch (m.data)[(m.p)] { - case 45: - goto st13 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st13 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st13 - } - default: - goto st13 - } - goto tr6 - st13: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof13 - } - stCase13: - switch (m.data)[(m.p)] { - case 45: - goto st14 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st14 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st14 - } - default: - goto st14 - } - goto tr6 - st14: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof14 - } - stCase14: - switch (m.data)[(m.p)] { - case 45: - goto st15 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st15 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st15 - } - default: - goto st15 - } - goto tr6 - st15: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof15 - } - stCase15: - switch (m.data)[(m.p)] { - case 45: - goto st16 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st16 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st16 - } - default: - goto st16 - } - goto tr6 - st16: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof16 - } - stCase16: - switch (m.data)[(m.p)] { - case 45: - goto st17 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st17 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st17 - } - default: - goto st17 - } - goto tr6 - st17: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof17 - } - stCase17: - switch (m.data)[(m.p)] { - case 45: - goto st18 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st18 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st18 - } - default: - goto st18 - } - goto tr6 - st18: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof18 - } - stCase18: - switch (m.data)[(m.p)] { - case 45: - goto st19 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st19 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st19 - } - default: - goto st19 - } - goto tr6 - st19: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof19 - } - stCase19: - switch (m.data)[(m.p)] { - case 45: - goto st20 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st20 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st20 - } - default: - goto st20 - } - goto tr6 - st20: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof20 - } - stCase20: - switch (m.data)[(m.p)] { - case 45: - goto st21 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st21 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st21 - } - default: - goto st21 - } - goto tr6 - st21: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof21 - } - stCase21: - switch (m.data)[(m.p)] { - case 45: - goto st22 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st22 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st22 - } - default: - goto st22 - } - goto tr6 - st22: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof22 - } - stCase22: - switch (m.data)[(m.p)] { - case 45: - goto st23 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st23 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st23 - } - default: - goto st23 - } - goto tr6 - st23: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof23 - } - stCase23: - switch (m.data)[(m.p)] { - case 45: - goto st24 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st24 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st24 - } - default: - goto st24 - } - goto tr6 - st24: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof24 - } - stCase24: - switch (m.data)[(m.p)] { - case 45: - goto st25 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st25 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st25 - } - default: - goto st25 - } - goto tr6 - st25: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof25 - } - stCase25: - switch (m.data)[(m.p)] { - case 45: - goto st26 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st26 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st26 - } - default: - goto st26 - } - goto tr6 - st26: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof26 - } - stCase26: - switch (m.data)[(m.p)] { - case 45: - goto st27 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st27 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st27 - } - default: - goto st27 - } - goto tr6 - st27: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof27 - } - stCase27: - switch (m.data)[(m.p)] { - case 45: - goto st28 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st28 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st28 - } - default: - goto st28 - } - goto tr6 - st28: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof28 - } - stCase28: - switch (m.data)[(m.p)] { - case 45: - goto st29 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st29 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st29 - } - default: - goto st29 - } - goto tr6 - st29: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof29 - } - stCase29: - switch (m.data)[(m.p)] { - case 45: - goto st30 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st30 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st30 - } - default: - goto st30 - } - goto tr6 - st30: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof30 - } - stCase30: - switch (m.data)[(m.p)] { - case 45: - goto st31 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st31 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st31 - } - default: - goto st31 - } - goto tr6 - st31: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof31 - } - stCase31: - switch (m.data)[(m.p)] { - case 45: - goto st32 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st32 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st32 - } - default: - goto st32 - } - goto tr6 - st32: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof32 - } - stCase32: - switch (m.data)[(m.p)] { - case 45: - goto st33 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st33 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st33 - } - default: - goto st33 - } - goto tr6 - st33: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof33 - } - stCase33: - switch (m.data)[(m.p)] { - case 45: - goto st34 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st34 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st34 - } - default: - goto st34 - } - goto tr6 - st34: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof34 - } - stCase34: - switch (m.data)[(m.p)] { - case 45: - goto st35 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st35 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st35 - } - default: - goto st35 - } - goto tr6 - st35: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof35 - } - stCase35: - switch (m.data)[(m.p)] { - case 45: - goto st36 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st36 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st36 - } - default: - goto st36 - } - goto tr6 - st36: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof36 - } - stCase36: - switch (m.data)[(m.p)] { - case 45: - goto st37 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st37 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st37 - } - default: - goto st37 - } - goto tr6 - st37: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof37 - } - stCase37: - if (m.data)[(m.p)] == 58 { - goto tr10 - } - goto tr6 - tr10: + goto _again + f51: - output.ID = string(m.text()) + if output.qStart { + m.err = fmt.Errorf(err8141QComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again - goto st38 - st38: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof38 } - stCase38: - switch (m.data)[(m.p)] { + output.qStart = true + + output.qComponent = string(m.text()) + + goto _again + f46: + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + f29: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + goto _again + + _again: + switch _toStateActions[m.cs] { case 33: - goto tr42 - case 36: - goto tr42 - case 37: - goto tr43 - case 61: - goto tr42 - case 95: - goto tr42 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto tr42 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto tr42 - } - case (m.data)[(m.p)] >= 64: - goto tr42 - } - default: - goto tr42 - } - goto tr41 - tr42: - m.pb = m.p + (m.p)-- - goto st44 - st44: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof44 + m.err = fmt.Errorf(err8141InformalID, m.p) + m.cs = 193 + goto _again } - stCase44: - switch (m.data)[(m.p)] { - case 33: - goto st44 - case 36: - goto st44 - case 37: - goto st39 - case 61: - goto st44 - case 95: - goto st44 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto st44 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st44 - } - case (m.data)[(m.p)] >= 64: - goto st44 - } - default: - goto st44 - } - goto tr41 - tr43: - m.pb = m.p - - goto st39 - st39: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof39 + if m.cs == 0 { + goto _out } - stCase39: - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st40 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st40 - } - default: - goto tr46 + if (m.p)++; (m.p) != (m.pe) { + goto _resume } - goto tr44 - tr46: - - m.tolower = append(m.tolower, m.p-m.pb) - - goto st40 - st40: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof40 - } - stCase40: - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st45 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st45 - } - default: - goto tr48 - } - goto tr44 - tr48: - - m.tolower = append(m.tolower, m.p-m.pb) - - goto st45 - st45: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof45 - } - stCase45: - switch (m.data)[(m.p)] { - case 33: - goto st44 - case 36: - goto st44 - case 37: - goto st39 - case 61: - goto st44 - case 95: - goto st44 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto st44 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st44 - } - case (m.data)[(m.p)] >= 64: - goto st44 - } - default: - goto st44 - } - goto tr44 - tr8: - - m.pb = m.p - - goto st41 - st41: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof41 - } - stCase41: - switch (m.data)[(m.p)] { - case 45: - goto st7 - case 58: - goto tr10 - case 82: - goto st42 - case 114: - goto st42 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st7 - } - default: - goto st7 - } - goto tr6 - st42: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof42 - } - stCase42: - switch (m.data)[(m.p)] { - case 45: - goto st8 - case 58: - goto tr10 - case 78: - goto st43 - case 110: - goto st43 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st8 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st8 - } - default: - goto st8 - } - goto tr50 - st43: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof43 - } - stCase43: - if (m.data)[(m.p)] == 45 { - goto st9 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st9 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st9 - } - default: - goto st9 - } - goto tr52 - st46: - if (m.p)++; (m.p) == (m.pe) { - goto _testEof46 - } - stCase46: - switch (m.data)[(m.p)] { - case 10: - goto st0 - case 13: - goto st0 - } - goto st46 - stOut: - _testEof2: - m.cs = 2 - goto _testEof - _testEof3: - m.cs = 3 - goto _testEof - _testEof4: - m.cs = 4 - goto _testEof - _testEof5: - m.cs = 5 - goto _testEof - _testEof6: - m.cs = 6 - goto _testEof - _testEof7: - m.cs = 7 - goto _testEof - _testEof8: - m.cs = 8 - goto _testEof - _testEof9: - m.cs = 9 - goto _testEof - _testEof10: - m.cs = 10 - goto _testEof - _testEof11: - m.cs = 11 - goto _testEof - _testEof12: - m.cs = 12 - goto _testEof - _testEof13: - m.cs = 13 - goto _testEof - _testEof14: - m.cs = 14 - goto _testEof - _testEof15: - m.cs = 15 - goto _testEof - _testEof16: - m.cs = 16 - goto _testEof - _testEof17: - m.cs = 17 - goto _testEof - _testEof18: - m.cs = 18 - goto _testEof - _testEof19: - m.cs = 19 - goto _testEof - _testEof20: - m.cs = 20 - goto _testEof - _testEof21: - m.cs = 21 - goto _testEof - _testEof22: - m.cs = 22 - goto _testEof - _testEof23: - m.cs = 23 - goto _testEof - _testEof24: - m.cs = 24 - goto _testEof - _testEof25: - m.cs = 25 - goto _testEof - _testEof26: - m.cs = 26 - goto _testEof - _testEof27: - m.cs = 27 - goto _testEof - _testEof28: - m.cs = 28 - goto _testEof - _testEof29: - m.cs = 29 - goto _testEof - _testEof30: - m.cs = 30 - goto _testEof - _testEof31: - m.cs = 31 - goto _testEof - _testEof32: - m.cs = 32 - goto _testEof - _testEof33: - m.cs = 33 - goto _testEof - _testEof34: - m.cs = 34 - goto _testEof - _testEof35: - m.cs = 35 - goto _testEof - _testEof36: - m.cs = 36 - goto _testEof - _testEof37: - m.cs = 37 - goto _testEof - _testEof38: - m.cs = 38 - goto _testEof - _testEof44: - m.cs = 44 - goto _testEof - _testEof39: - m.cs = 39 - goto _testEof - _testEof40: - m.cs = 40 - goto _testEof - _testEof45: - m.cs = 45 - goto _testEof - _testEof41: - m.cs = 41 - goto _testEof - _testEof42: - m.cs = 42 - goto _testEof - _testEof43: - m.cs = 43 - goto _testEof - _testEof46: - m.cs = 46 - goto _testEof - _testEof: { } if (m.p) == (m.eof) { - switch m.cs { - case 44, 45: - - raw := m.text() - output.SS = string(raw) - // Iterate upper letters lowering them - for _, i := range m.tolower { - raw[i] = raw[i] + 32 - } - output.norm = string(raw) - - case 1, 2, 4: - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 3: + switch _eofActions[m.cs] { + case 1: m.err = fmt.Errorf(errPrefix, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 41: + case 6: m.err = fmt.Errorf(errIdentifier, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 38: + case 8: m.err = fmt.Errorf(errSpecificString, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + case 24: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - m.err = fmt.Errorf(errParse, m.p) + case 12: + + m.err = fmt.Errorf(errSCIMNamespace, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - case 42: + case 14: - m.err = fmt.Errorf(errPrefix, m.p) + m.err = fmt.Errorf(errSCIMType, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + case 16: + + m.err = fmt.Errorf(errSCIMName, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 18: + + if m.p == m.pe { + m.err = fmt.Errorf(errSCIMOtherIncomplete, m.p-1) + } else { + m.err = fmt.Errorf(errSCIMOther, m.p) } + (m.p)-- + + m.cs = 193 + goto _again + + case 23: + + m.err = fmt.Errorf(err8141SpecificString, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 22: + + m.err = fmt.Errorf(err8141Identifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 26: + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 31: + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 34: + + output.SS = string(m.text()) + // Iterate upper letters lowering them + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 + } + + output.kind = RFC2141 + + case 38: + + output.SS = string(m.text()) + // Iterate upper letters lowering them + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 + } + + output.kind = RFC8141 + + case 4: m.err = fmt.Errorf(errIdentifier, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - m.err = fmt.Errorf(errParse, m.p) + m.err = fmt.Errorf(errPrefix, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - case 43: + case 11: + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again m.err = fmt.Errorf(errNoUrnWithinID, m.p) (m.p)-- - { - goto st46 - } + m.cs = 193 + goto _again - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- + case 9: - { - goto st46 - } + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- + m.cs = 193 + goto _again - { - goto st46 - } - - case 39, 40: - - m.err = fmt.Errorf(errHex, m.p) - (m.p)-- - - { - goto st46 } m.err = fmt.Errorf(errSpecificString, m.p) (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + case 20: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } - m.err = fmt.Errorf(errParse, m.p) + if m.p == m.pe { + m.err = fmt.Errorf(errSCIMOtherIncomplete, m.p-1) + } else { + m.err = fmt.Errorf(errSCIMOther, m.p) + } (m.p)-- - { - goto st46 + m.cs = 193 + goto _again + + case 25: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + } + + m.err = fmt.Errorf(err8141SpecificString, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 28: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 29: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 21: + + m.err = fmt.Errorf(err8141Identifier, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 42: + + output.rComponent = string(m.text()) + + output.kind = RFC8141 + + case 48: + + output.qComponent = string(m.text()) + + output.kind = RFC8141 + + case 41: + + output.fComponent = string(m.text()) + + output.kind = RFC8141 + + case 40: + + m.pb = m.p + + output.fComponent = string(m.text()) + + output.kind = RFC8141 + + case 30: + + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + case 35: + + output.scim.Name = string(m.data[output.scim.pos:m.p]) + + output.SS = string(m.text()) + // Iterate upper letters lowering them + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 + } + + output.kind = RFC7643 + + case 37: + + output.scim.Other = string(m.data[output.scim.pos:m.p]) + + output.SS = string(m.text()) + // Iterate upper letters lowering them + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 + } + + output.kind = RFC7643 + + case 44: + + if output.rStart { + m.err = fmt.Errorf(err8141RComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + output.rStart = true + + output.rComponent = string(m.text()) + + output.kind = RFC8141 + + case 50: + + if output.qStart { + m.err = fmt.Errorf(err8141QComponentStart, m.p) + (m.p)-- + + m.cs = 193 + goto _again + + } + output.qStart = true + + output.qComponent = string(m.text()) + + output.kind = RFC8141 } } @@ -1686,3 +5031,16 @@ func (m *machine) Parse(input []byte) (*URN, error) { return output, nil } + +func (m *machine) WithParsingMode(x ParsingMode) { + m.parsingMode = x + switch m.parsingMode { + case RFC2141Only: + m.startParsingAt = enMain + case RFC8141Only: + m.startParsingAt = enRfc8141Only + case RFC7643Only: + m.startParsingAt = enScimOnly + } + m.parsingModeSet = true +} diff --git a/vendor/github.com/leodido/go-urn/machine.go.rl b/vendor/github.com/leodido/go-urn/machine.go.rl index 3bc05a651..0a1742199 100644 --- a/vendor/github.com/leodido/go-urn/machine.go.rl +++ b/vendor/github.com/leodido/go-urn/machine.go.rl @@ -2,15 +2,28 @@ package urn import ( "fmt" + + scimschema "github.com/leodido/go-urn/scim/schema" ) var ( - errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" - errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" - errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" - errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" - errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" - errParse = "parsing error [col %d]" + errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" + errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its beginning) [col %d]" + errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" + errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" + errHex = "expecting the percent encoded chars to be well-formed (%%alnum{2}) [col %d]" + errSCIMNamespace = "expecing the SCIM namespace identifier (ietf:params:scim) [col %d]" + errSCIMType = "expecting a correct SCIM type (schemas, api, param) [col %d]" + errSCIMName = "expecting one or more alnum char in the SCIM name part [col %d]" + errSCIMOther = "expecting a well-formed other SCIM part [col %d]" + errSCIMOtherIncomplete = "expecting a not empty SCIM other part after colon [col %d]" + err8141InformalID = "informal URN namespace must be in the form urn-[1-9][0-9] [col %d]" + err8141SpecificString = "expecting the specific string to contain alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] not in first position) chars [col %d]" + err8141Identifier = "expecting the indentifier to be a string with (length 2 to 32 chars) containing alnum (or dashes) not starting or ending with a dash [col %d]" + err8141RComponentStart = "expecting only one r-component (starting with the ?+ sequence) [col %d]" + err8141QComponentStart = "expecting only one q-component (starting with the ?= sequence) [col %d]" + err8141MalformedRComp = "expecting a non-empty r-component containing alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] but not at its beginning) [col %d]" + err8141MalformedQComp = "expecting a non-empty q-component containing alnum, hex, or others ([~&()+,-.:=@;$_!*'] or [/?] but not at its beginning) [col %d]" ) %%{ @@ -24,25 +37,42 @@ action mark { } action tolower { - m.tolower = append(m.tolower, m.p - m.pb) + // List of positions in the buffer to later lowercase + output.tolower = append(output.tolower, m.p - m.pb) } action set_pre { output.prefix = string(m.text()) } +action throw_pre_urn_err { + if m.parsingMode != RFC8141Only { + // Throw an error when: + // - we are entering here matching the the prefix in the namespace identifier part + // - looking ahead (3 chars) we find a colon + if pos := m.p + 3; pos < m.pe && m.data[pos] == 58 && output.prefix != "" { + m.err = fmt.Errorf(errNoUrnWithinID, pos) + fhold; + fgoto fail; + } + } +} + action set_nid { output.ID = string(m.text()) } action set_nss { - raw := m.text() - output.SS = string(raw) + output.SS = string(m.text()) // Iterate upper letters lowering them - for _, i := range m.tolower { - raw[i] = raw[i] + 32 + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] + 32 + } + output.norm = string(m.text()) + // Revert the buffer to the original + for _, i := range output.tolower { + m.data[m.pb+i] = m.data[m.pb+i] - 32 } - output.norm = string(raw) } action err_pre { @@ -70,20 +100,20 @@ action err_urn { } action err_hex { - m.err = fmt.Errorf(errHex, m.p) - fhold; - fgoto fail; + if m.parsingMode == RFC2141Only || m.parsingMode == RFC8141Only { + m.err = fmt.Errorf(errHex, m.p) + fhold; + fgoto fail; + } } -action err_parse { - m.err = fmt.Errorf(errParse, m.p) - fhold; - fgoto fail; +action base_type { + output.kind = RFC2141; } -pre = ([uU][rR][nN] @err(err_pre)) >mark %set_pre; +pre = ([uU] @err(err_pre) [rR] @err(err_pre) [nN] @err(err_pre)) >mark >throw_pre_urn_err %set_pre; -nid = (alnum >mark (alnum | '-'){0,31}) %set_nid; +nid = (alnum >mark (alnum | '-'){0,31}) $err(err_nid) %set_nid; hex = '%' (digit | lower | upper >tolower){2} $err(err_hex); @@ -91,9 +121,179 @@ sss = (alnum | [()+,\-.:=@;$_!*']); nss = (sss | hex)+ $err(err_nss); +nid_not_urn = (nid - pre %err(err_urn)); + +urn = pre ':' @err(err_pre) (nid_not_urn ':' nss >mark %set_nss) %eof(base_type); + +### SCIM BEG + +action err_scim_nid { + m.err = fmt.Errorf(errSCIMNamespace, m.p) + fhold; + fgoto fail; +} + +action err_scim_type { + m.err = fmt.Errorf(errSCIMType, m.p) + fhold; + fgoto fail; +} + +action err_scim_name { + m.err = fmt.Errorf(errSCIMName, m.p) + fhold; + fgoto fail; +} + +action err_scim_other { + if m.p == m.pe { + m.err = fmt.Errorf(errSCIMOtherIncomplete, m.p-1) + } else { + m.err = fmt.Errorf(errSCIMOther, m.p) + } + fhold; + fgoto fail; +} + +action scim_type { + output.kind = RFC7643; +} + +action create_scim { + output.scim = &SCIM{}; +} + +action set_scim_type { + output.scim.Type = scimschema.TypeFromString(string(m.text())) +} + +action mark_scim_name { + output.scim.pos = m.p +} + +action set_scim_name { + output.scim.Name = string(m.data[output.scim.pos:m.p]) +} + +action mark_scim_other { + output.scim.pos = m.p +} + +action set_scim_other { + output.scim.Other = string(m.data[output.scim.pos:m.p]) +} + +scim_nid = 'ietf:params:scim' >mark %set_nid %create_scim $err(err_scim_nid); + +scim_other = ':' (sss | hex)+ >mark_scim_other %set_scim_other $err(err_scim_other); + +scim_name = (alnum)+ >mark_scim_name %set_scim_name $err(err_scim_name); + +scim_type = ('schemas' | 'api' | 'param') >mark %set_scim_type $err(err_scim_type); + +scim_only := pre ':' @err(err_pre) (scim_nid ':' scim_type ':' scim_name scim_other? %set_nss) %eof(scim_type); + +### SCIM END + +### 8141 BEG + +action err_nss_8141 { + m.err = fmt.Errorf(err8141SpecificString, m.p) + fhold; + fgoto fail; +} + +action err_nid_8141 { + m.err = fmt.Errorf(err8141Identifier, m.p) + fhold; + fgoto fail; +} + +action rfc8141_type { + output.kind = RFC8141; +} + +action set_r_component { + output.rComponent = string(m.text()) +} + +action set_q_component { + output.qComponent = string(m.text()) +} + +action set_f_component { + output.fComponent = string(m.text()) +} + +action informal_nid_match { + fhold; + m.err = fmt.Errorf(err8141InformalID, m.p); + fgoto fail; +} + +action mark_r_start { + if output.rStart { + m.err = fmt.Errorf(err8141RComponentStart, m.p) + fhold; + fgoto fail; + } + output.rStart = true +} + +action mark_q_start { + if output.qStart { + m.err = fmt.Errorf(err8141QComponentStart, m.p) + fhold; + fgoto fail; + } + output.qStart = true +} + +action err_malformed_r_component { + m.err = fmt.Errorf(err8141MalformedRComp, m.p) + fhold; + fgoto fail; +} + +action err_malformed_q_component { + m.err = fmt.Errorf(err8141MalformedQComp, m.p) + fhold; + fgoto fail; +} + +pchar = (sss | '~' | '&' | hex); + +component = pchar (pchar | '/' | '?')*; + +r_start = ('?+') %mark_r_start; + +r_component = r_start <: (r_start | component)+ $err(err_malformed_r_component) >mark %set_r_component; + +q_start = ('?=') %mark_q_start; + +q_component = q_start <: (q_start | component)+ $err(err_malformed_q_component) >mark %set_q_component; + +rq_components = (r_component :>> q_component? | q_component); + +fragment = (pchar | '/' | '?')*; + +f_component = '#' fragment >mark %set_f_component; + +nss_rfc8141 = (pchar >mark (pchar | '/')*) $err(err_nss_8141) %set_nss; + +nid_rfc8141 = (alnum >mark (alnum | '-'){0,30} alnum) $err(err_nid_8141) %set_nid; + +informal_id = pre ('-' [a-zA-z0] %to(informal_nid_match)); + +nid_rfc8141_not_urn = (nid_rfc8141 - informal_id?); + +rfc8141_only := pre ':' @err(err_pre) nid_rfc8141_not_urn ':' nss_rfc8141 rq_components? f_component? %eof(rfc8141_type); + +### 8141 END + fail := (any - [\n\r])* @err{ fgoto main; }; -main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) $err(err_parse); +main := urn; }%% @@ -103,6 +303,7 @@ main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) type Machine interface { Error() error Parse(input []byte) (*URN, error) + WithParsingMode(ParsingMode) } type machine struct { @@ -110,12 +311,24 @@ type machine struct { cs int p, pe, eof, pb int err error - tolower []int + startParsingAt int + parsingMode ParsingMode + parsingModeSet bool } // NewMachine creates a new FSM able to parse RFC 2141 strings. -func NewMachine() Machine { - m := &machine{} +func NewMachine(options ...Option) Machine { + m := &machine{ + parsingModeSet: false, + } + + for _, o := range options { + o(m) + } + // Set default parsing mode + if !m.parsingModeSet { + m.WithParsingMode(DefaultParsingMode) + } %% access m.; %% variable p m.p; @@ -137,7 +350,7 @@ func (m *machine) text() []byte { return m.data[m.pb:m.p] } -// Parse parses the input byte array as a RFC 2141 string. +// Parse parses the input byte array as a RFC 2141 or RFC7643 string. func (m *machine) Parse(input []byte) (*URN, error) { m.data = input m.p = 0 @@ -145,10 +358,11 @@ func (m *machine) Parse(input []byte) (*URN, error) { m.pe = len(input) m.eof = len(input) m.err = nil - m.tolower = []int{} - output := &URN{} + m.cs = m.startParsingAt + output := &URN{ + tolower: []int{}, + } - %% write init; %% write exec; if m.cs < first_final || m.cs == en_fail { @@ -157,3 +371,16 @@ func (m *machine) Parse(input []byte) (*URN, error) { return output, nil } + +func (m *machine) WithParsingMode(x ParsingMode) { + m.parsingMode = x + switch m.parsingMode { + case RFC2141Only: + m.startParsingAt = en_main + case RFC8141Only: + m.startParsingAt = en_rfc8141_only + case RFC7643Only: + m.startParsingAt = en_scim_only + } + m.parsingModeSet = true +} \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/makefile b/vendor/github.com/leodido/go-urn/makefile index df87cdc6d..68d5dd0f1 100644 --- a/vendor/github.com/leodido/go-urn/makefile +++ b/vendor/github.com/leodido/go-urn/makefile @@ -15,18 +15,24 @@ clean: .PHONY: images images: docs/urn.png +.PHONY: snake2camel +snake2camel: + @cd ./tools/snake2camel; go build -o ../../snake2camel . + .PHONY: removecomments removecomments: @cd ./tools/removecomments; go build -o ../../removecomments . machine.go: machine.go.rl +machine.go: snake2camel + machine.go: removecomments machine.go: - $(RAGEL) -Z -G2 -e -o $@ $< + $(RAGEL) -Z -G1 -e -o $@ $< @./removecomments $@ - $(MAKE) -s file=$@ snake2camel + @./snake2camel $@ $(GOFMT) $@ docs/urn.dot: machine.go.rl @@ -41,13 +47,5 @@ bench: *_test.go machine.go go test -bench=. -benchmem -benchtime=5s ./... .PHONY: tests -tests: *_test.go +tests: *_test.go $(GO_TEST) ./... - -.PHONY: snake2camel -snake2camel: - @awk -i inplace '{ \ - while ( match($$0, /(.*)([a-z]+[0-9]*)_([a-zA-Z0-9])(.*)/, cap) ) \ - $$0 = cap[1] cap[2] toupper(cap[3]) cap[4]; \ - print \ - }' $(file) diff --git a/vendor/github.com/leodido/go-urn/options.go b/vendor/github.com/leodido/go-urn/options.go new file mode 100644 index 000000000..c543835a2 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/options.go @@ -0,0 +1,9 @@ +package urn + +type Option func(Machine) + +func WithParsingMode(mode ParsingMode) Option { + return func(m Machine) { + m.WithParsingMode(mode) + } +} diff --git a/vendor/github.com/leodido/go-urn/parsing_mode.go b/vendor/github.com/leodido/go-urn/parsing_mode.go new file mode 100644 index 000000000..fce5aadc3 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/parsing_mode.go @@ -0,0 +1,12 @@ +package urn + +type ParsingMode int + +const ( + Default ParsingMode = iota + RFC2141Only + RFC7643Only + RFC8141Only +) + +const DefaultParsingMode = RFC2141Only diff --git a/vendor/github.com/leodido/go-urn/scim.go b/vendor/github.com/leodido/go-urn/scim.go new file mode 100644 index 000000000..f6b7aefba --- /dev/null +++ b/vendor/github.com/leodido/go-urn/scim.go @@ -0,0 +1,48 @@ +package urn + +import ( + "encoding/json" + "fmt" + + scimschema "github.com/leodido/go-urn/scim/schema" +) + +const errInvalidSCIMURN = "invalid SCIM URN: %s" + +type SCIM struct { + Type scimschema.Type + Name string + Other string + pos int +} + +func (s SCIM) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +func (s *SCIM) UnmarshalJSON(bytes []byte) error { + var str string + if err := json.Unmarshal(bytes, &str); err != nil { + return err + } + // Parse as SCIM + value, ok := Parse([]byte(str), WithParsingMode(RFC7643Only)) + if !ok { + return fmt.Errorf(errInvalidSCIMURN, str) + } + if value.RFC() != RFC7643 { + return fmt.Errorf(errInvalidSCIMURN, str) + } + *s = *value.SCIM() + + return nil +} + +func (s *SCIM) String() string { + ret := fmt.Sprintf("urn:ietf:params:scim:%s:%s", s.Type.String(), s.Name) + if s.Other != "" { + ret += fmt.Sprintf(":%s", s.Other) + } + + return ret +} diff --git a/vendor/github.com/leodido/go-urn/scim/schema/type.go b/vendor/github.com/leodido/go-urn/scim/schema/type.go new file mode 100644 index 000000000..134918230 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/scim/schema/type.go @@ -0,0 +1,36 @@ +package scimschema + +type Type int + +const ( + Unsupported Type = iota + Schemas + API + Param +) + +func (t Type) String() string { + switch t { + case Schemas: + return "schemas" + case API: + return "api" + case Param: + return "param" + } + + return "" +} + +func TypeFromString(input string) Type { + switch input { + case "schemas": + return Schemas + case "api": + return API + case "param": + return Param + } + + return Unsupported +} diff --git a/vendor/github.com/leodido/go-urn/urn.go b/vendor/github.com/leodido/go-urn/urn.go index d51a6c915..894d6258d 100644 --- a/vendor/github.com/leodido/go-urn/urn.go +++ b/vendor/github.com/leodido/go-urn/urn.go @@ -16,10 +16,18 @@ const errInvalidURN = "invalid URN: %s" // // Details at https://tools.ietf.org/html/rfc2141. type URN struct { - prefix string // Static prefix. Equal to "urn" when empty. - ID string // Namespace identifier - SS string // Namespace specific string - norm string // Normalized namespace specific string + prefix string // Static prefix. Equal to "urn" when empty. + ID string // Namespace identifier (NID) + SS string // Namespace specific string (NSS) + norm string // Normalized namespace specific string + kind Kind + scim *SCIM + rComponent string // RFC8141 + qComponent string // RFC8141 + fComponent string // RFC8141 + rStart bool // RFC8141 + qStart bool // RFC8141 + tolower []int } // Normalize turns the receiving URN into its norm version. @@ -30,12 +38,21 @@ func (u *URN) Normalize() *URN { prefix: "urn", ID: strings.ToLower(u.ID), SS: u.norm, + // rComponent: u.rComponent, + // qComponent: u.qComponent, + // fComponent: u.fComponent, } } // Equal checks the lexical equivalence of the current URN with another one. func (u *URN) Equal(x *URN) bool { - return *u.Normalize() == *x.Normalize() + if x == nil { + return false + } + nu := u.Normalize() + nx := x.Normalize() + + return nu.prefix == nx.prefix && nu.ID == nx.ID && nu.SS == nx.SS } // String reassembles the URN into a valid URN string. @@ -51,14 +68,23 @@ func (u *URN) String() string { res += "urn" } res += u.prefix + ":" + u.ID + ":" + u.SS + if u.rComponent != "" { + res += "?+" + u.rComponent + } + if u.qComponent != "" { + res += "?=" + u.qComponent + } + if u.fComponent != "" { + res += "#" + u.fComponent + } } return res } -// Parse is responsible to create an URN instance from a byte array matching the correct URN syntax. -func Parse(u []byte) (*URN, bool) { - urn, err := NewMachine().Parse(u) +// Parse is responsible to create an URN instance from a byte array matching the correct URN syntax (RFC 2141). +func Parse(u []byte, options ...Option) (*URN, bool) { + urn, err := NewMachine(options...).Parse(u) if err != nil { return nil, false } @@ -71,7 +97,7 @@ func (u URN) MarshalJSON() ([]byte, error) { return json.Marshal(u.String()) } -// MarshalJSON unmarshals a URN from JSON string form (e.g. `"urn:oid:1.2.3.4"`). +// UnmarshalJSON unmarshals a URN from JSON string form (e.g. `"urn:oid:1.2.3.4"`). func (u *URN) UnmarshalJSON(bytes []byte) error { var str string if err := json.Unmarshal(bytes, &str); err != nil { @@ -82,5 +108,34 @@ func (u *URN) UnmarshalJSON(bytes []byte) error { } else { *u = *value } + return nil -} \ No newline at end of file +} + +func (u *URN) IsSCIM() bool { + return u.kind == RFC7643 +} + +func (u *URN) SCIM() *SCIM { + if u.kind != RFC7643 { + return nil + } + + return u.scim +} + +func (u *URN) RFC() Kind { + return u.kind +} + +func (u *URN) FComponent() string { + return u.fComponent +} + +func (u *URN) QComponent() string { + return u.qComponent +} + +func (u *URN) RComponent() string { + return u.rComponent +} diff --git a/vendor/github.com/leodido/go-urn/urn8141.go b/vendor/github.com/leodido/go-urn/urn8141.go new file mode 100644 index 000000000..da4dd062e --- /dev/null +++ b/vendor/github.com/leodido/go-urn/urn8141.go @@ -0,0 +1,30 @@ +package urn + +import ( + "encoding/json" + "fmt" +) + +const errInvalidURN8141 = "invalid URN per RFC 8141: %s" + +type URN8141 struct { + *URN +} + +func (u URN8141) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} + +func (u *URN8141) UnmarshalJSON(bytes []byte) error { + var str string + if err := json.Unmarshal(bytes, &str); err != nil { + return err + } + if value, ok := Parse([]byte(str), WithParsingMode(RFC8141Only)); !ok { + return fmt.Errorf(errInvalidURN8141, str) + } else { + *u = URN8141{value} + } + + return nil +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/accounts.go b/vendor/github.com/nats-io/nats-server/v2/server/accounts.go index b45cec6f9..216bf6623 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/accounts.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/accounts.go @@ -1877,6 +1877,10 @@ func (a *Account) addServiceImport(dest *Account, from, to string, claim *jwt.Im rt := Singleton var lat *serviceLatency + if dest == nil { + return nil, ErrMissingAccount + } + dest.mu.RLock() se := dest.getServiceExport(to) if se != nil { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/auth_callout.go b/vendor/github.com/nats-io/nats-server/v2/server/auth_callout.go index 359914c63..6620a4e51 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/auth_callout.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/auth_callout.go @@ -230,7 +230,6 @@ func (s *Server) processClientOrLeafCallout(c *client, opts *Options) (authorize } } } - return targetAcc, nil } @@ -270,6 +269,14 @@ func (s *Server) processClientOrLeafCallout(c *client, opts *Options) (authorize return } + // the JWT is cleared, because if in operator mode it may hold the JWT + // for the bearer token that connected to the callout if in operator mode + // the permissions are already set on the client, this prevents a decode + // on c.RegisterNKeyUser which would have wrong values + c.mu.Lock() + c.opts.JWT = _EMPTY_ + c.mu.Unlock() + // Build internal user and bind to the targeted account. nkuser := buildInternalNkeyUser(arc, allowedConnTypes, targetAcc) if err := c.RegisterNkeyUser(nkuser); err != nil { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/client.go b/vendor/github.com/nats-io/nats-server/v2/server/client.go index 4ef424cbb..348c0478f 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/client.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/client.go @@ -4089,6 +4089,12 @@ func getHeader(key string, hdr []byte) []byte { return value } +// For bytes.HasPrefix below. +var ( + jsRequestNextPreB = []byte(jsRequestNextPre) + jsDirectGetPreB = []byte(jsDirectGetPre) +) + // processServiceImport is an internal callback when a subscription matches an imported service // from another account. This includes response mappings as well. func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byte) { @@ -4110,8 +4116,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt var checkJS bool shouldReturn := si.invalid || acc.sl == nil if !shouldReturn && !isResponse && si.to == jsAllAPI { - subj := bytesToString(c.pa.subject) - if strings.HasPrefix(subj, jsRequestNextPre) || strings.HasPrefix(subj, jsDirectGetPre) { + if bytes.HasPrefix(c.pa.subject, jsDirectGetPreB) || bytes.HasPrefix(c.pa.subject, jsRequestNextPreB) { checkJS = true } } @@ -4790,7 +4795,11 @@ func (c *client) processPingTimer() { var sendPing bool - pingInterval := c.srv.getOpts().PingInterval + opts := c.srv.getOpts() + pingInterval := opts.PingInterval + if c.kind == ROUTER && opts.Cluster.PingInterval > 0 { + pingInterval = opts.Cluster.PingInterval + } pingInterval = adjustPingInterval(c.kind, pingInterval) now := time.Now() needRTT := c.rtt == 0 || now.Sub(c.rttStart) > DEFAULT_RTT_MEASUREMENT_INTERVAL @@ -4810,7 +4819,11 @@ func (c *client) processPingTimer() { if sendPing { // Check for violation - if c.ping.out+1 > c.srv.getOpts().MaxPingsOut { + maxPingsOut := opts.MaxPingsOut + if c.kind == ROUTER && opts.Cluster.MaxPingsOut > 0 { + maxPingsOut = opts.Cluster.MaxPingsOut + } + if c.ping.out+1 > maxPingsOut { c.Debugf("Stale Client Connection - Closing") c.enqueueProto([]byte(fmt.Sprintf(errProto, "Stale Connection"))) c.mu.Unlock() @@ -4847,7 +4860,11 @@ func (c *client) setPingTimer() { if c.srv == nil { return } - d := c.srv.getOpts().PingInterval + opts := c.srv.getOpts() + d := opts.PingInterval + if c.kind == ROUTER && opts.Cluster.PingInterval > 0 { + d = opts.Cluster.PingInterval + } d = adjustPingInterval(c.kind, d) c.ping.tmr = time.AfterFunc(d, c.processPingTimer) } @@ -5740,6 +5757,9 @@ func (c *client) setFirstPingTimer() { opts := s.getOpts() d := opts.PingInterval + if c.kind == ROUTER && opts.Cluster.PingInterval > 0 { + d = opts.Cluster.PingInterval + } if !opts.DisableShortFirstPing { if c.kind != CLIENT { if d > firstPingInterval { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/const.go b/vendor/github.com/nats-io/nats-server/v2/server/const.go index dea5c9e9b..238a2e5df 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/const.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/const.go @@ -41,7 +41,7 @@ var ( const ( // VERSION is the current version for the server. - VERSION = "2.10.9" + VERSION = "2.10.10" // PROTO is the currently supported protocol. // 0 was the original diff --git a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go index 02a8443e7..a9ad0ebc4 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go @@ -4384,8 +4384,9 @@ func (o *consumer) trackPending(sseq, dseq uint64) { o.ptmr = time.AfterFunc(o.ackWait(0), o.checkPending) } if p, ok := o.pending[sseq]; ok { + // Update timestamp but keep original consumer delivery sequence. + // So do not update p.Sequence. p.Timestamp = time.Now().UnixNano() - p.Sequence = dseq } else { o.pending[sseq] = &Pending{dseq, time.Now().UnixNano()} } @@ -4606,6 +4607,8 @@ func (o *consumer) checkPending() { o.rdq = nil o.rdqi.Empty() o.pending = nil + // Mimic behavior in processAckMsg when pending is empty. + o.adflr, o.asflr = o.dseq-1, o.sseq-1 } // Update our state if needed. @@ -4936,6 +4939,7 @@ func (o *consumer) purge(sseq uint64, slseq uint64) { // This means we can reset everything at this point. if len(o.pending) == 0 { o.pending, o.rdc = nil, nil + o.adflr, o.asflr = o.dseq-1, o.sseq-1 } // We need to remove all those being queued for redelivery under o.rdq @@ -5330,33 +5334,42 @@ func (o *consumer) isMonitorRunning() bool { return o.inMonitor } +// If we detect that our ackfloor is higher than the stream's last sequence, return this error. +var errAckFloorHigherThanLastSeq = errors.New("consumer ack floor is higher than streams last sequence") + // If we are a consumer of an interest or workqueue policy stream, process that state and make sure consistent. -func (o *consumer) checkStateForInterestStream() { - o.mu.Lock() +func (o *consumer) checkStateForInterestStream() error { + o.mu.RLock() // See if we need to process this update if our parent stream is not a limits policy stream. mset := o.mset shouldProcessState := mset != nil && o.retention != LimitsPolicy if o.closed || !shouldProcessState { - o.mu.Unlock() - return + o.mu.RUnlock() + return nil } state, err := o.store.State() - o.mu.Unlock() + o.mu.RUnlock() if err != nil { - return + return err } asflr := state.AckFloor.Stream // Protect ourselves against rolling backwards. if asflr&(1<<63) != 0 { - return + return nil } // We should make sure to update the acks. var ss StreamState mset.store.FastState(&ss) + // Check if the underlying stream's last sequence is less than our floor. + // This can happen if the stream has been reset and has not caught up yet. + if asflr > ss.LastSeq { + return errAckFloorHigherThanLastSeq + } + for seq := ss.FirstSeq; seq <= asflr; seq++ { mset.ackMsg(o, seq) } @@ -5374,4 +5387,5 @@ func (o *consumer) checkStateForInterestStream() { } } } + return nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/events.go b/vendor/github.com/nats-io/nats-server/v2/server/events.go index 66d744d45..391e677ca 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/events.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/events.go @@ -1625,7 +1625,6 @@ func (s *Server) shutdownEventing() { // internal send loop to exit. s.sendShutdownEvent() wg.Wait() - close(rc) s.mu.Lock() defer s.mu.Unlock() @@ -1637,6 +1636,9 @@ func (s *Server) shutdownEventing() { }) // Turn everything off here. s.sys = nil + // Make sure this is done after s.sys = nil, so that we don't + // get sends to closed channels on badly-timed config reloads. + close(rc) } // Request for our local connection count. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go index 8b73d353d..861fe4dc9 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go @@ -40,6 +40,7 @@ import ( "github.com/klauspost/compress/s2" "github.com/minio/highwayhash" "github.com/nats-io/nats-server/v2/server/avl" + "github.com/nats-io/nats-server/v2/server/stree" "golang.org/x/crypto/chacha20" "golang.org/x/crypto/chacha20poly1305" ) @@ -176,13 +177,14 @@ type fileStore struct { lmb *msgBlock blks []*msgBlock bim map[uint32]*msgBlock - psim map[string]*psi + psim *stree.SubjectTree[psi] tsl int adml int hh hash.Hash64 qch chan struct{} fch chan struct{} fsld chan struct{} + cmu sync.RWMutex cfs []ConsumerStore sips int dirty int @@ -382,7 +384,7 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim fs := &fileStore{ fcfg: fcfg, - psim: make(map[string]*psi), + psim: stree.NewSubjectTree[psi](), bim: make(map[uint32]*msgBlock), cfg: FileStreamInfo{Created: created, StreamConfig: cfg}, prf: prf, @@ -428,7 +430,7 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim prior := fs.state // Reset anything that could have been set from above. fs.state = StreamState{} - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 fs.bim = make(map[uint32]*msgBlock) fs.blks = nil fs.tombs = nil @@ -744,7 +746,10 @@ func (fs *fileStore) setupAEK() error { if _, err := os.Stat(keyFile); err != nil && !os.IsNotExist(err) { return err } - if err := os.WriteFile(keyFile, encrypted, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(keyFile, encrypted, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } // Set our aek. @@ -775,14 +780,20 @@ func (fs *fileStore) writeStreamMeta() error { b = fs.aek.Seal(nonce, nonce, b, nil) } - if err := os.WriteFile(meta, b, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(meta, b, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } fs.hh.Reset() fs.hh.Write(b) checksum := hex.EncodeToString(fs.hh.Sum(nil)) sum := filepath.Join(fs.fcfg.StoreDir, JetStreamMetaFileSum) - if err := os.WriteFile(sum, []byte(checksum), defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(sum, []byte(checksum), defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } return nil @@ -847,7 +858,7 @@ const ( // Lock should be held. func (fs *fileStore) noTrackSubjects() bool { - return !(len(fs.psim) > 0 || len(fs.cfg.Subjects) > 0 || fs.cfg.Mirror != nil || len(fs.cfg.Sources) > 0) + return !(fs.psim.Size() > 0 || len(fs.cfg.Subjects) > 0 || fs.cfg.Mirror != nil || len(fs.cfg.Sources) > 0) } // Will init the basics for a message block. @@ -950,7 +961,7 @@ func (fs *fileStore) recoverMsgBlock(index uint32) (*msgBlock, error) { // Open up the message file, but we will try to recover from the index file. // We will check that the last checksums match. - file, err := os.Open(mb.mfn) + file, err := mb.openBlock() if err != nil { return nil, err } @@ -1174,11 +1185,16 @@ func (mb *msgBlock) convertCipher() error { // the old keyfile back. if err := fs.genEncryptionKeysForBlock(mb); err != nil { keyFile := filepath.Join(mdir, fmt.Sprintf(keyScan, mb.index)) + <-dios os.WriteFile(keyFile, ekey, defaultFilePerms) + dios <- struct{}{} return err } mb.bek.XORKeyStream(buf, buf) - if err := os.WriteFile(mb.mfn, buf, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(mb.mfn, buf, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } return nil @@ -1203,7 +1219,10 @@ func (mb *msgBlock) convertToEncrypted() error { // Undo cache from above for later. mb.cache = nil mb.bek.XORKeyStream(buf, buf) - if err := os.WriteFile(mb.mfn, buf, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(mb.mfn, buf, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } return nil @@ -1285,7 +1304,9 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, []uint64, error) { if mb.mfd != nil { fd = mb.mfd } else { + <-dios fd, err = os.OpenFile(mb.mfn, os.O_RDWR, defaultFilePerms) + dios <- struct{}{} if err == nil { defer fd.Close() } @@ -1577,7 +1598,7 @@ func (fs *fileStore) recoverFullState() (rerr error) { // Check for per subject info. if numSubjects := int(readU64()); numSubjects > 0 { - fs.psim, fs.tsl = make(map[string]*psi, numSubjects), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 for i := 0; i < numSubjects; i++ { if lsubj := int(readU64()); lsubj > 0 { if bi+lsubj > len(buf) { @@ -1588,23 +1609,23 @@ func (fs *fileStore) recoverFullState() (rerr error) { // If we have lots of subjects this will alloc for each one. // We could reference the underlying buffer, but we could guess wrong if // number of blocks is large and subjects is low, since we would reference buf. - subj := string(buf[bi : bi+lsubj]) + subj := buf[bi : bi+lsubj] // We had a bug that could cause memory corruption in the PSIM that could have gotten stored to disk. // Only would affect subjects, so do quick check. - if !isValidSubject(subj, true) { + if !isValidSubject(string(subj), true) { os.Remove(fn) fs.warn("Stream state corrupt subject detected") return errCorruptState } bi += lsubj - psi := &psi{total: readU64(), fblk: uint32(readU64())} + psi := psi{total: readU64(), fblk: uint32(readU64())} if psi.total > 1 { psi.lblk = uint32(readU64()) } else { psi.lblk = psi.fblk } - fs.psim[subj] = psi - fs.tsl += len(subj) + fs.psim.Insert(subj, psi) + fs.tsl += lsubj } } } @@ -1769,13 +1790,13 @@ func (fs *fileStore) adjustAccounting(mb, nmb *msgBlock) { fs.state.Msgs++ fs.state.Bytes += fileStoreMsgSize(sm.subj, sm.hdr, sm.msg) if len(sm.subj) > 0 && fs.psim != nil { - if info, ok := fs.psim[sm.subj]; ok { + if info, ok := fs.psim.Find(stringToBytes(sm.subj)); ok { info.total++ if nmb.index > info.lblk { info.lblk = nmb.index } } else { - fs.psim[sm.subj] = &psi{total: 1, fblk: nmb.index, lblk: nmb.index} + fs.psim.Insert(stringToBytes(sm.subj), psi{total: 1, fblk: nmb.index, lblk: nmb.index}) fs.tsl += len(sm.subj) } } @@ -1801,7 +1822,7 @@ func (fs *fileStore) adjustAccounting(mb, nmb *msgBlock) { // Grabs last checksum for the named block file. // Takes into account encryption etc. func (mb *msgBlock) lastChecksum() []byte { - f, err := os.Open(mb.mfn) + f, err := mb.openBlock() if err != nil { return nil } @@ -1843,7 +1864,9 @@ func (fs *fileStore) cleanupOldMeta() { mdir := filepath.Join(fs.fcfg.StoreDir, msgDir) fs.mu.RUnlock() + <-dios f, err := os.Open(mdir) + dios <- struct{}{} if err != nil { return } @@ -2142,7 +2165,7 @@ func (fs *fileStore) expireMsgsOnRecover() { lmb.writeTombstone(last.seq, last.ts) } // Clear any global subject state. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 } // If we purged anything, make sure we kick flush state loop. @@ -2414,6 +2437,9 @@ func (fs *fileStore) FilteredState(sseq uint64, subj string) SimpleState { // If past the end no results. if sseq > lseq { + // Make sure we track sequences + ss.First = fs.state.FirstSeq + ss.Last = fs.state.LastSeq return ss } @@ -2458,55 +2484,42 @@ func (fs *fileStore) numFilteredPending(filter string, ss *SimpleState) { return } - tsa := [32]string{} - fsa := [32]string{} - fts := tokenizeSubjectIntoSlice(fsa[:0], filter) - start, stop := uint32(math.MaxUint32), uint32(0) - for subj, psi := range fs.psim { - if isAll { - ss.Msgs += psi.total - } else { - tts := tokenizeSubjectIntoSlice(tsa[:0], subj) - if isSubsetMatchTokenized(tts, fts) { - ss.Msgs += psi.total - // Keep track of start and stop indexes for this subject. - if psi.fblk < start { - start = psi.fblk - } - if psi.lblk > stop { - stop = psi.lblk - } + fs.psim.Match(stringToBytes(filter), func(_ []byte, psi *psi) { + ss.Msgs += psi.total + // Keep track of start and stop indexes for this subject. + if psi.fblk < start { + start = psi.fblk + } + if psi.lblk > stop { + stop = psi.lblk + } + }) + // We do need to figure out the first and last sequences. + wc := subjectHasWildcard(filter) + // Do start + mb := fs.bim[start] + if mb != nil { + _, f, _ := mb.filteredPending(filter, wc, 0) + ss.First = f + } + if ss.First == 0 { + // This is a miss. This can happen since psi.fblk is lazy, but should be very rare. + for i := start + 1; i <= stop; i++ { + mb := fs.bim[i] + if mb == nil { + continue + } + if _, f, _ := mb.filteredPending(filter, wc, 0); f > 0 { + ss.First = f + break } } } - // If not collecting all we do need to figure out the first and last sequences. - if !isAll { - wc := subjectHasWildcard(filter) - // Do start - mb := fs.bim[start] - if mb != nil { - _, f, _ := mb.filteredPending(filter, wc, 0) - ss.First = f - } - if ss.First == 0 { - // This is a miss. This can happen since psi.fblk is lazy, but should be very rare. - for i := start + 1; i <= stop; i++ { - mb := fs.bim[i] - if mb == nil { - continue - } - if _, f, _ := mb.filteredPending(filter, wc, 0); f > 0 { - ss.First = f - break - } - } - } - // Now last - if mb = fs.bim[stop]; mb != nil { - _, _, l := mb.filteredPending(filter, wc, 0) - ss.Last = l - } + // Now last + if mb = fs.bim[stop]; mb != nil { + _, _, l := mb.filteredPending(filter, wc, 0) + ss.Last = l } } @@ -2522,8 +2535,8 @@ func (fs *fileStore) SubjectsState(subject string) map[string]SimpleState { start, stop := fs.blks[0], fs.lmb // We can short circuit if not a wildcard using psim for start and stop. if !subjectHasWildcard(subject) { - info := fs.psim[subject] - if info == nil { + info, ok := fs.psim.Find(stringToBytes(subject)) + if !ok { return nil } start, stop = fs.bim[info.fblk], fs.bim[info.lblk] @@ -2604,10 +2617,14 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) wc := subjectHasWildcard(filter) // See if filter was provided but its the only subject. - if !isAll && !wc && len(fs.psim) == 1 && fs.psim[filter] != nil { - isAll = true + if !isAll && !wc && fs.psim.Size() == 1 { + if _, ok := fs.psim.Find(stringToBytes(filter)); ok { + isAll = true + } + } + if isAll && filter == _EMPTY_ { + filter = fwcs } - // If we are isAll and have no deleted we can do a simpler calculation. if !lastPerSubject && isAll && (fs.state.LastSeq-fs.state.FirstSeq+1) == fs.state.Msgs { if sseq == 0 { @@ -2638,7 +2655,7 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) if lastPerSubject { // If we want all and our start sequence is equal or less than first return number of subjects. if isAll && sseq <= fs.state.FirstSeq { - return uint64(len(fs.psim)), validThrough + return uint64(fs.psim.Size()), validThrough } // If we are here we need to scan. We are going to scan the PSIM looking for lblks that are >= seqStart. // This will build up a list of all subjects from the selected block onward. @@ -2646,20 +2663,19 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) mb := fs.blks[seqStart] bi := mb.index - for subj, psi := range fs.psim { + fs.psim.Match(stringToBytes(filter), func(subj []byte, psi *psi) { // If the select blk start is greater than entry's last blk skip. if bi > psi.lblk { - continue + return } - if isMatch(subj) { - total++ - // We will track the subjects that are an exact match to the last block. - // This is needed for last block processing. - if psi.lblk == bi { - lbm[subj] = true - } + total++ + // We will track the subjects that are an exact match to the last block. + // This is needed for last block processing. + if psi.lblk == bi { + lbm[string(subj)] = true } - } + }) + // Now check if we need to inspect the seqStart block. // Grab write lock in case we need to load in msgs. mb.mu.Lock() @@ -2768,15 +2784,13 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) // If we are here it's better to calculate totals from psim and adjust downward by scanning less blocks. // TODO(dlc) - Eventually when sublist uses generics, make this sublist driven instead. start := uint32(math.MaxUint32) - for subj, psi := range fs.psim { - if isMatch(subj) { - total += psi.total - // Keep track of start index for this subject. - if psi.fblk < start { - start = psi.fblk - } + fs.psim.Match(stringToBytes(filter), func(_ []byte, psi *psi) { + total += psi.total + // Keep track of start index for this subject. + if psi.fblk < start { + start = psi.fblk } - } + }) // See if we were asked for all, if so we are done. if sseq <= fs.state.FirstSeq { return total, validThrough @@ -2860,30 +2874,14 @@ func (fs *fileStore) SubjectsTotals(filter string) map[string]uint64 { fs.mu.RLock() defer fs.mu.RUnlock() - if len(fs.psim) == 0 { + if fs.psim.Size() == 0 { return nil } - tsa := [32]string{} - fsa := [32]string{} - fts := tokenizeSubjectIntoSlice(fsa[:0], filter) - isAll := filter == _EMPTY_ || filter == fwcs - wc := subjectHasWildcard(filter) - - isMatch := func(subj string) bool { - if !wc { - return subj == filter - } - tts := tokenizeSubjectIntoSlice(tsa[:0], subj) - return isSubsetMatchTokenized(tts, fts) - } - fst := make(map[string]uint64) - for subj, psi := range fs.psim { - if isAll || isMatch(subj) { - fst[subj] = psi.total - } - } + fs.psim.Match(stringToBytes(filter), func(subj []byte, psi *psi) { + fst[string(subj)] = psi.total + }) return fst } @@ -2970,7 +2968,10 @@ func (fs *fileStore) newMsgBlockForWrite() (*msgBlock, error) { } mb.hh = hh + <-dios mfd, err := os.OpenFile(mb.mfn, os.O_CREATE|os.O_RDWR, defaultFilePerms) + dios <- struct{}{} + if err != nil { mb.dirtyCloseWithRemove(true) return nil, fmt.Errorf("Error creating msg block file: %v", err) @@ -3014,7 +3015,10 @@ func (fs *fileStore) genEncryptionKeysForBlock(mb *msgBlock) error { if _, err := os.Stat(keyFile); err != nil && !os.IsNotExist(err) { return err } - if err := os.WriteFile(keyFile, encrypted, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(keyFile, encrypted, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } mb.kfn = keyFile @@ -3033,7 +3037,7 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts in var psmc uint64 psmax := mmp > 0 && len(subj) > 0 if psmax { - if info, ok := fs.psim[subj]; ok { + if info, ok := fs.psim.Find(stringToBytes(subj)); ok { psmc = info.total } } @@ -3079,13 +3083,13 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts in // Adjust top level tracking of per subject msg counts. if len(subj) > 0 && fs.psim != nil { index := fs.lmb.index - if info, ok := fs.psim[subj]; ok { + if info, ok := fs.psim.Find(stringToBytes(subj)); ok { info.total++ if index > info.lblk { info.lblk = index } } else { - fs.psim[subj] = &psi{total: 1, fblk: index, lblk: index} + fs.psim.Insert(stringToBytes(subj), psi{total: 1, fblk: index, lblk: index}) fs.tsl += len(subj) } } @@ -3112,7 +3116,8 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts in if ok, _ := fs.removeMsgViaLimits(fseq); ok { // Make sure we are below the limit. if psmc--; psmc >= mmp { - for info, ok := fs.psim[subj]; ok && info.total > mmp; info, ok = fs.psim[subj] { + bsubj := stringToBytes(subj) + for info, ok := fs.psim.Find(bsubj); ok && info.total > mmp; info, ok = fs.psim.Find(bsubj) { if seq, _ := fs.firstSeqForSubj(subj); seq > 0 { if ok, _ := fs.removeMsgViaLimits(seq); !ok { break @@ -3362,7 +3367,7 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) { // See if we can optimize where we start. start, stop := fs.blks[0].index, fs.lmb.index - if info, ok := fs.psim[subj]; ok { + if info, ok := fs.psim.Find(stringToBytes(subj)); ok { start, stop = info.fblk, info.lblk } @@ -3384,7 +3389,7 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) { if ss := mb.fss[subj]; ss != nil { // Adjust first if it was not where we thought it should be. if i != start { - if info, ok := fs.psim[subj]; ok { + if info, ok := fs.psim.Find(stringToBytes(subj)); ok { info.fblk = i } } @@ -3457,19 +3462,20 @@ func (fs *fileStore) enforceMsgPerSubjectLimit(fireCallback bool) { // collect all that are not correct. needAttention := make(map[string]*psi) - for subj, psi := range fs.psim { + fs.psim.Iter(func(subj []byte, psi *psi) bool { numMsgs += psi.total if psi.total > maxMsgsPer { - needAttention[subj] = psi + needAttention[string(subj)] = psi } - } + return true + }) // We had an issue with a use case where psim (and hence fss) were correct but idx was not and was not properly being caught. // So do a quick sanity check here. If we detect a skew do a rebuild then re-check. if numMsgs != fs.state.Msgs { fs.warn("Detected skew in subject-based total (%d) vs raw total (%d), rebuilding", numMsgs, fs.state.Msgs) // Clear any global subject state. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 for _, mb := range fs.blks { ld, _, err := mb.rebuildState() if err != nil && ld != nil { @@ -3481,11 +3487,12 @@ func (fs *fileStore) enforceMsgPerSubjectLimit(fireCallback bool) { fs.rebuildStateLocked(nil) // Need to redo blocks that need attention. needAttention = make(map[string]*psi) - for subj, psi := range fs.psim { + fs.psim.Iter(func(subj []byte, psi *psi) bool { if psi.total > maxMsgsPer { - needAttention[subj] = psi + needAttention[string(subj)] = psi } - } + return true + }) } // Collect all the msgBlks we alter. @@ -3569,13 +3576,15 @@ func (fs *fileStore) removePerSubject(subj string) { return } // We do not update sense of fblk here but will do so when we resolve during lookup. - if info, ok := fs.psim[subj]; ok { + bsubj := stringToBytes(subj) + if info, ok := fs.psim.Find(bsubj); ok { info.total-- if info.total == 1 { info.fblk = info.lblk } else if info.total == 0 { - delete(fs.psim, subj) - fs.tsl -= len(subj) + if _, ok = fs.psim.Delete(bsubj); ok { + fs.tsl -= len(subj) + } } } } @@ -3902,7 +3911,10 @@ func (mb *msgBlock) compact() { // We will write to a new file and mv/rename it in case of failure. mfn := filepath.Join(mb.fs.fcfg.StoreDir, msgDir, fmt.Sprintf(newScan, mb.index)) - if err := os.WriteFile(mfn, nbuf, defaultFilePerms); err != nil { + <-dios + err := os.WriteFile(mfn, nbuf, defaultFilePerms) + dios <- struct{}{} + if err != nil { os.Remove(mfn) return } @@ -3943,11 +3955,24 @@ func (mb *msgBlock) slotInfo(slot int) (uint32, uint32, bool, error) { // Determine record length var rl uint32 - if len(mb.cache.idx) > slot+1 { - ni := mb.cache.idx[slot+1] &^ hbit - rl = ni - ri - } else { + if slot >= len(mb.cache.idx) { rl = mb.cache.lrl + } else { + // Need to account for dbit markers in idx. + // So we will walk until we find valid idx slot to calculate rl. + for i := 1; slot+i < len(mb.cache.idx); i++ { + ni := mb.cache.idx[slot+i] &^ hbit + if ni == dbit { + continue + } + rl = ni - ri + break + } + // check if we had all trailing dbits. + // If so use len of cache buf minus ri. + if rl == 0 { + rl = uint32(len(mb.cache.buf)) - ri + } } if rl < msgHdrSize { return 0, 0, false, errBadMsg @@ -4092,7 +4117,9 @@ func (mb *msgBlock) eraseMsg(seq uint64, ri, rl int) error { // Disk if mb.cache.off+mb.cache.wp > ri { + <-dios mfd, err := os.OpenFile(mb.mfn, os.O_RDWR, defaultFilePerms) + dios <- struct{}{} if err != nil { return err } @@ -4523,7 +4550,7 @@ func (fs *fileStore) checkMsgs() *LostStreamData { fs.checkAndFlushAllBlocks() // Clear any global subject state. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 for _, mb := range fs.blks { // Make sure encryption loaded if needed for the block. @@ -4547,7 +4574,9 @@ func (mb *msgBlock) enableForWriting(fip bool) error { if mb.mfd != nil { return nil } + <-dios mfd, err := os.OpenFile(mb.mfn, os.O_CREATE|os.O_RDWR, defaultFilePerms) + dios <- struct{}{} if err != nil { return fmt.Errorf("error opening msg block file [%q]: %v", mb.mfn, err) } @@ -4817,11 +4846,6 @@ func (fs *fileStore) writeMsgRecord(seq uint64, ts int64, subj string, hdr, msg } func (mb *msgBlock) recompressOnDiskIfNeeded() error { - // Wait for disk I/O slots to become available. This prevents us from - // running away with system resources. - <-dios - defer func() { dios <- struct{}{} }() - alg := mb.fs.fcfg.Compression mb.mu.Lock() defer mb.mu.Unlock() @@ -4835,7 +4859,10 @@ func (mb *msgBlock) recompressOnDiskIfNeeded() error { // header, in which case we do nothing. // 2. The block will be uncompressed, in which case we will compress it // and then write it back out to disk, reencrypting if necessary. + <-dios origBuf, err := os.ReadFile(origFN) + dios <- struct{}{} + if err != nil { return fmt.Errorf("failed to read original block from disk: %w", err) } @@ -4880,7 +4907,9 @@ func (mb *msgBlock) recompressOnDiskIfNeeded() error { // operation if something goes wrong), create a new temporary file. We will // write out the new block here and then swap the files around afterwards // once everything else has succeeded correctly. + <-dios tmpFD, err := os.OpenFile(tmpFN, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, defaultFilePerms) + dios <- struct{}{} if err != nil { return fmt.Errorf("failed to create temporary file: %w", err) } @@ -4970,7 +4999,7 @@ func (mb *msgBlock) ensureRawBytesLoaded() error { if mb.rbytes > 0 { return nil } - f, err := os.Open(mb.mfn) + f, err := mb.openBlock() if err != nil { return err } @@ -5042,7 +5071,11 @@ func (fs *fileStore) syncBlocks() { // Check if we need to sync. // This is done not holding any locks. if needSync { - if fd, _ := os.OpenFile(fn, os.O_RDWR, defaultFilePerms); fd != nil { + <-dios + fd, _ := os.OpenFile(fn, os.O_RDWR, defaultFilePerms) + dios <- struct{}{} + // If we have an fd. + if fd != nil { canClear := fd.Sync() == nil fd.Close() // Only clear sync flag on success. @@ -5070,7 +5103,10 @@ func (fs *fileStore) syncBlocks() { // Sync state file if we are not running with sync always. if !syncAlways { - if fd, _ := os.OpenFile(fn, os.O_RDWR, defaultFilePerms); fd != nil { + <-dios + fd, _ := os.OpenFile(fn, os.O_RDWR, defaultFilePerms) + dios <- struct{}{} + if fd != nil { fd.Sync() fd.Close() } @@ -5157,10 +5193,11 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { var index uint32 mbFirstSeq := atomic.LoadUint64(&mb.first.seq) + mbLastSeq := atomic.LoadUint64(&mb.last.seq) // Capture beginning size of dmap. dms := uint64(mb.dmap.Size()) - idxSz := atomic.LoadUint64(&mb.last.seq) - mbFirstSeq + 1 + idxSz := mbLastSeq - mbFirstSeq + 1 if mb.cache == nil { // Approximation, may adjust below. @@ -5185,12 +5222,14 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { } lbuf := uint32(len(buf)) + var seq uint64 for index < lbuf { if index+msgHdrSize > lbuf { return errCorruptState } hdr := buf[index : index+msgHdrSize] - rl, seq, slen := le.Uint32(hdr[0:]), le.Uint64(hdr[4:]), int(le.Uint16(hdr[20:])) + rl, slen := le.Uint32(hdr[0:]), int(le.Uint16(hdr[20:])) + seq = le.Uint64(hdr[4:]) // Clear any headers bit that could be set. rl &^= hbit @@ -5257,6 +5296,19 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { index += rl } + // Track holes at the end of the block, these would be missed in the + // earlier loop if we've ran out of block file to look at, but should + // be easily noticed because the seq will be below the last seq from + // the index. + if seq > 0 && seq < mbLastSeq { + for dseq := seq; dseq < mbLastSeq; dseq++ { + idx = append(idx, dbit) + if dms == 0 { + mb.dmap.Insert(dseq) + } + } + } + mb.cache.buf = buf mb.cache.idx = idx mb.cache.fseq = fseq @@ -5442,6 +5494,16 @@ func (mb *msgBlock) fssLoaded() bool { return mb.fss != nil } +// Wrap openBlock for the gated semaphore processing. +// Lock should be held +func (mb *msgBlock) openBlock() (*os.File, error) { + // Gate with concurrent IO semaphore. + <-dios + f, err := os.Open(mb.mfn) + dios <- struct{}{} + return f, err +} + // Used to load in the block contents. // Lock should be held and all conditionals satisfied prior. func (mb *msgBlock) loadBlock(buf []byte) ([]byte, error) { @@ -5456,7 +5518,7 @@ func (mb *msgBlock) loadBlock(buf []byte) ([]byte, error) { } if f == nil { var err error - f, err = os.Open(mb.mfn) + f, err = mb.openBlock() if err != nil { if os.IsNotExist(err) { err = errNoBlkData @@ -5665,7 +5727,7 @@ const ( ebit = 1 << 63 // Used for marking tombstone sequences. tbit = 1 << 62 - // Used to mark a bad index as deleted. + // Used to mark an index as deleted and non-existent. dbit = 1 << 30 ) @@ -5890,7 +5952,7 @@ func (fs *fileStore) loadLast(subj string, sm *StoreMsg) (lsm *StoreMsg, err err wc := subjectHasWildcard(subj) // If literal subject check for presence. if !wc { - if info := fs.psim[subj]; info == nil { + if info, ok := fs.psim.Find(stringToBytes(subj)); !ok { return nil, ErrStoreMsgNotFound } else { start, stop = info.lblk, info.fblk @@ -5960,6 +6022,16 @@ func (fs *fileStore) LoadNextMsg(filter string, wc bool, start uint64, sm *Store start = fs.state.FirstSeq } + // If start is less than or equal to beginning of our stream, meaning our first call, + // let's check the psim to see if we can skip ahead. + if start <= fs.state.FirstSeq { + var ss SimpleState + fs.numFilteredPending(filter, &ss) + if ss.First > start { + start = ss.First + } + } + if bi, _ := fs.selectMsgBlockWithIndex(start); bi >= 0 { for i := bi; i < len(fs.blks); i++ { mb := fs.blks[i] @@ -5987,7 +6059,14 @@ func (fs *fileStore) Type() StorageType { // Returns number of subjects in this store. // Lock should be held. func (fs *fileStore) numSubjects() int { - return len(fs.psim) + return fs.psim.Size() +} + +// numConsumers uses new lock. +func (fs *fileStore) numConsumers() int { + fs.cmu.RLock() + defer fs.cmu.RUnlock() + return len(fs.cfs) } // FastState will fill in state with only the following. @@ -6006,7 +6085,7 @@ func (fs *fileStore) FastState(state *StreamState) { state.NumDeleted = 0 } } - state.Consumers = len(fs.cfs) + state.Consumers = fs.numConsumers() state.NumSubjects = fs.numSubjects() fs.mu.RUnlock() } @@ -6015,7 +6094,7 @@ func (fs *fileStore) FastState(state *StreamState) { func (fs *fileStore) State() StreamState { fs.mu.RLock() state := fs.state - state.Consumers = len(fs.cfs) + state.Consumers = fs.numConsumers() state.NumSubjects = fs.numSubjects() state.Deleted = nil // make sure. @@ -6443,7 +6522,7 @@ func (fs *fileStore) purge(fseq uint64) (uint64, error) { fs.lmb = nil fs.bim = make(map[uint32]*msgBlock) // Clear any per subject tracking. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 // Mark dirty fs.dirty++ @@ -6454,14 +6533,25 @@ func (fs *fileStore) purge(fseq uint64) (uint64, error) { // If purge directory still exists then we need to wait // in place and remove since rename would fail. if _, err := os.Stat(pdir); err == nil { + <-dios os.RemoveAll(pdir) + dios <- struct{}{} } - os.Rename(mdir, pdir) - go os.RemoveAll(pdir) + <-dios + os.Rename(mdir, pdir) + dios <- struct{}{} + + go func() { + <-dios + os.RemoveAll(pdir) + dios <- struct{}{} + }() // Create new one. + <-dios os.MkdirAll(mdir, defaultDirPerms) + dios <- struct{}{} // Make sure we have a lmb to write to. if _, err := fs.newMsgBlockForWrite(); err != nil { @@ -6629,7 +6719,10 @@ func (fs *fileStore) Compact(seq uint64) (uint64, error) { if nbuf, err = smb.cmp.Compress(nbuf); err != nil { goto SKIP } - if err = os.WriteFile(smb.mfn, nbuf, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(smb.mfn, nbuf, defaultFilePerms) + dios <- struct{}{} + if err != nil { goto SKIP } // Make sure to remove fss state. @@ -6718,7 +6811,7 @@ func (fs *fileStore) reset() error { fs.blks, fs.lmb = nil, nil // Reset subject mappings. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 fs.bim = make(map[uint32]*msgBlock) // If we purged anything, make sure we kick flush state loop. @@ -7005,7 +7098,7 @@ func (mb *msgBlock) recalculateFirstForSubj(subj string, startSeq uint64, ss *Si // Lock should be held. func (fs *fileStore) resetGlobalPerSubjectInfo() { // Clear any global subject state. - fs.psim, fs.tsl = make(map[string]*psi), 0 + fs.psim, fs.tsl = fs.psim.Empty(), 0 for _, mb := range fs.blks { fs.populateGlobalPerSubjectInfo(mb) } @@ -7096,13 +7189,14 @@ func (fs *fileStore) populateGlobalPerSubjectInfo(mb *msgBlock) { // Now populate psim. for subj, ss := range mb.fss { if len(subj) > 0 { - if info, ok := fs.psim[subj]; ok { + bsubj := stringToBytes(subj) + if info, ok := fs.psim.Find(bsubj); ok { info.total += ss.Msgs if mb.index > info.lblk { info.lblk = mb.index } } else { - fs.psim[subj] = &psi{total: ss.Msgs, fblk: mb.index, lblk: mb.index} + fs.psim.Insert(bsubj, psi{total: ss.Msgs, fblk: mb.index, lblk: mb.index}) fs.tsl += len(subj) } } @@ -7312,7 +7406,7 @@ func (fs *fileStore) writeFullState() error { } // For calculating size. - numSubjects := len(fs.psim) + numSubjects := fs.psim.Size() // Calculate and estimate of the uper bound on the size to avoid multiple allocations. sz := 2 + // Magic and Version @@ -7324,11 +7418,12 @@ func (fs *fileStore) writeFullState() error { binary.MaxVarintLen64 + 8 + 8 // last index + record checksum + full state checksum // Do 4k on stack if possible. - var raw [4 * 1024]byte + const ssz = 4 * 1024 var buf []byte - if sz <= cap(raw) { - buf, sz = raw[0:2:cap(raw)], cap(raw) + if sz <= ssz { + var _buf [ssz]byte + buf, sz = _buf[0:2:ssz], ssz } else { buf = make([]byte, hdrLen, sz) } @@ -7344,7 +7439,7 @@ func (fs *fileStore) writeFullState() error { // Do per subject information map if applicable. buf = binary.AppendUvarint(buf, uint64(numSubjects)) if numSubjects > 0 { - for subj, psi := range fs.psim { + fs.psim.Match([]byte(fwcs), func(subj []byte, psi *psi) { buf = binary.AppendUvarint(buf, uint64(len(subj))) buf = append(buf, subj...) buf = binary.AppendUvarint(buf, psi.total) @@ -7352,7 +7447,7 @@ func (fs *fileStore) writeFullState() error { if psi.total > 1 { buf = binary.AppendUvarint(buf, uint64(psi.lblk)) } - } + }) } // Now walk all blocks and write out first and last and optional dmap encoding. @@ -7521,11 +7616,13 @@ func (fs *fileStore) stop(writeState bool) error { // We should update the upper usage layer on a stop. cb, bytes := fs.scb, int64(fs.state.Bytes) + fs.mu.Unlock() + fs.cmu.Lock() var _cfs [256]ConsumerStore cfs := append(_cfs[:0], fs.cfs...) fs.cfs = nil - fs.mu.Unlock() + fs.cmu.Unlock() for _, o := range cfs { o.Stop() @@ -7673,9 +7770,9 @@ func (fs *fileStore) streamSnapshot(w io.WriteCloser, state *StreamState, includ } // Do consumers' state last. - fs.mu.RLock() + fs.cmu.RLock() cfs := fs.cfs - fs.mu.RUnlock() + fs.cmu.RUnlock() for _, cs := range cfs { o, ok := cs.(*consumerFileStore) @@ -8018,7 +8115,10 @@ func (fs *fileStore) ConsumerStore(name string, cfg *ConsumerConfig) (ConsumerSt // Redo the state file as well here if we have one and we can tell it was plaintext. if buf, err := os.ReadFile(o.ifn); err == nil { if _, err := decodeConsumerState(buf); err == nil { - if err := os.WriteFile(o.ifn, o.encryptState(buf), defaultFilePerms); err != nil { + <-dios + err := os.WriteFile(o.ifn, o.encryptState(buf), defaultFilePerms) + dios <- struct{}{} + if err != nil { if didCreate { os.RemoveAll(odir) } @@ -8508,7 +8608,10 @@ func (cfs *consumerFileStore) writeConsumerMeta() error { if _, err := os.Stat(keyFile); err != nil && !os.IsNotExist(err) { return err } - if err := os.WriteFile(keyFile, encrypted, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(keyFile, encrypted, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } } @@ -8524,14 +8627,21 @@ func (cfs *consumerFileStore) writeConsumerMeta() error { b = cfs.aek.Seal(nonce, nonce, b, nil) } - if err := os.WriteFile(meta, b, defaultFilePerms); err != nil { + <-dios + err = os.WriteFile(meta, b, defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } cfs.hh.Reset() cfs.hh.Write(b) checksum := hex.EncodeToString(cfs.hh.Sum(nil)) sum := filepath.Join(cfs.odir, JetStreamMetaFileSum) - if err := os.WriteFile(sum, []byte(checksum), defaultFilePerms); err != nil { + + <-dios + err = os.WriteFile(sum, []byte(checksum), defaultFilePerms) + dios <- struct{}{} + if err != nil { return err } return nil @@ -8617,7 +8727,10 @@ func (o *consumerFileStore) stateWithCopyLocked(doCopy bool) (*ConsumerState, er } // Read the state in here from disk.. + <-dios buf, err := os.ReadFile(o.ifn) + dios <- struct{}{} + if err != nil && !os.IsNotExist(err) { return nil, err } @@ -8883,15 +8996,15 @@ func (o *consumerFileStore) delete(streamDeleted bool) error { } func (fs *fileStore) AddConsumer(o ConsumerStore) error { - fs.mu.Lock() - defer fs.mu.Unlock() + fs.cmu.Lock() + defer fs.cmu.Unlock() fs.cfs = append(fs.cfs, o) return nil } func (fs *fileStore) RemoveConsumer(o ConsumerStore) error { - fs.mu.Lock() - defer fs.mu.Unlock() + fs.cmu.Lock() + defer fs.cmu.Unlock() for i, cfs := range fs.cfs { if o == cfs { fs.cfs = append(fs.cfs[:i], fs.cfs[i+1:]...) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go index 6b333878a..cba11d14e 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go @@ -1,4 +1,4 @@ -// Copyright 2019-2023 The NATS Authors +// Copyright 2019-2024 The NATS Authors // 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 @@ -653,9 +653,15 @@ func (a *Account) enableAllJetStreamServiceImportsAndMappings() error { } if !a.serviceImportExists(jsAllAPI) { - if err := a.AddServiceImport(s.SystemAccount(), jsAllAPI, _EMPTY_); err != nil { + // Capture si so we can turn on implicit sharing with JetStream layer. + // Make sure to set "to" otherwise will incur performance slow down. + si, err := a.addServiceImport(s.SystemAccount(), jsAllAPI, jsAllAPI, nil) + if err != nil { return fmt.Errorf("Error setting up jetstream service imports for account: %v", err) } + a.mu.Lock() + si.share = true + a.mu.Unlock() } // Check if we have a Domain specified. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go index 76c1c2a7b..14369d6c0 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go @@ -334,8 +334,8 @@ func generateJSMappingTable(domain string) map[string]string { // JSMaxDescription is the maximum description length for streams and consumers. const JSMaxDescriptionLen = 4 * 1024 -// JSMaxMetadataLen is the maximum length for streams an consumers metadata map. -// It's calculated by summing length of all keys an values. +// JSMaxMetadataLen is the maximum length for streams and consumers metadata map. +// It's calculated by summing length of all keys and values. const JSMaxMetadataLen = 128 * 1024 // JSMaxNameLen is the maximum name lengths for streams, consumers and templates. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go index 770b9957e..e7756cd11 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 The NATS Authors +// Copyright 2020-2024 The NATS Authors // 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 @@ -432,10 +432,10 @@ func (cc *jetStreamCluster) isStreamCurrent(account, stream string) bool { } // Restart the stream in question. -// Should only be called when the stream is know in a bad state. +// Should only be called when the stream is known to be in a bad state. func (js *jetStream) restartStream(acc *Account, csa *streamAssignment) { js.mu.Lock() - cc := js.cluster + s, cc := js.srv, js.cluster if cc == nil { js.mu.Unlock() return @@ -458,9 +458,18 @@ func (js *jetStream) restartStream(acc *Account, csa *streamAssignment) { } rg.node = nil } + sinceCreation := time.Since(sa.Created) js.mu.Unlock() // Process stream assignment to recreate. + // Check that we have given system enough time to start us up. + // This will be longer than obvious, and matches consumer logic in case system very busy. + if sinceCreation < 10*time.Second { + s.Debugf("Not restarting missing stream '%s > %s', too soon since creation %v", + acc, csa.Config.Name, sinceCreation) + return + } + js.processStreamAssignment(sa) // If we had consumers assigned to this server they will be present in the copy, csa. @@ -569,13 +578,24 @@ func (js *jetStream) isConsumerHealthy(mset *stream, consumer string, ca *consum // When we try to restart we nil out the node if applicable // and reprocess the consumer assignment. restartConsumer := func() { + mset.mu.RLock() + accName, streamName := mset.acc.GetName(), mset.cfg.Name + mset.mu.RUnlock() + js.mu.Lock() + deleted := ca.deleted + // Check that we have not just been created. + if !deleted && time.Since(ca.Created) < 10*time.Second { + s.Debugf("Not restarting missing consumer '%s > %s > %s', too soon since creation %v", + accName, streamName, consumer, time.Since(ca.Created)) + js.mu.Unlock() + return + } // Make sure the node is stopped if still running. if node != nil && node.State() != Closed { node.Stop() } ca.Group.node = nil - deleted := ca.deleted js.mu.Unlock() if !deleted { js.processConsumerAssignment(ca) @@ -597,7 +617,7 @@ func (js *jetStream) isConsumerHealthy(mset *stream, consumer string, ca *consum mset.mu.RLock() accName, streamName := mset.acc.GetName(), mset.cfg.Name mset.mu.RUnlock() - s.Warnf("Detected consumer cluster node skew '%s > %s'", accName, streamName, consumer) + s.Warnf("Detected consumer cluster node skew '%s > %s > %s'", accName, streamName, consumer) node.Delete() o.deleteWithoutAdvisory() restartConsumer() @@ -1123,6 +1143,7 @@ func (js *jetStream) checkForOrphans() { for accName, jsa := range js.accounts { asa := cc.streams[accName] + jsa.mu.RLock() for stream, mset := range jsa.streams { if sa := asa[stream]; sa == nil { streams = append(streams, mset) @@ -1136,6 +1157,7 @@ func (js *jetStream) checkForOrphans() { } } } + jsa.mu.RUnlock() } js.mu.Unlock() @@ -1356,7 +1378,7 @@ func (js *jetStream) monitorCluster() { case isLeader = <-lch: // For meta layer synchronize everyone to our state on becoming leader. - if isLeader { + if isLeader && n.ApplyQ().len() == 0 { n.SendSnapshot(js.metaSnapshot()) } // Process the change. @@ -2233,7 +2255,7 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps if err := n.InstallSnapshot(mset.stateSnapshot()); err == nil { lastState, lastSnapTime = curState, time.Now() - } else if err != errNoSnapAvailable && err != errNodeClosed { + } else if err != errNoSnapAvailable && err != errNodeClosed && err != errCatchupsRunning { s.RateLimitWarnf("Failed to install snapshot for '%s > %s' [%s]: %v", mset.acc.Name, mset.name(), n.Group(), err) } } @@ -2298,7 +2320,11 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps if mset.numConsumers() >= numExpectedConsumers { break } - time.Sleep(sleepTime) + select { + case <-s.quitCh: + return + case <-time.After(sleepTime): + } } if actual := mset.numConsumers(); actual < numExpectedConsumers { s.Warnf("All consumers not online for '%s > %s': expected %d but only have %d", accName, mset.name(), numExpectedConsumers, actual) @@ -2311,7 +2337,7 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps // We can arrive here NOT being the leader, so we send the snapshot only if we are, and in this case // reset the notion that we need to send the snapshot. If we are not, then the first time the server // will switch to leader (in the loop below), we will send the snapshot. - if sendSnapshot && isLeader && mset != nil && n != nil { + if sendSnapshot && isLeader && mset != nil && n != nil && !isRecovering { n.SendSnapshot(mset.stateSnapshot()) sendSnapshot = false } @@ -2331,9 +2357,14 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps // No special processing needed for when we are caught up on restart. if ce == nil { isRecovering = false - // Check on startup if we should snapshot/compact. - if _, b := n.Size(); b > compactSizeMin || n.NeedSnapshot() { - doSnapshot() + // Make sure we create a new snapshot in case things have changed such that any existing + // snapshot may no longer be valid. + doSnapshot() + // If we became leader during this time and we need to send a snapshot to our + // followers, i.e. as a result of a scale-up from R1, do it now. + if sendSnapshot && isLeader && mset != nil && n != nil { + n.SendSnapshot(mset.stateSnapshot()) + sendSnapshot = false } continue } @@ -2374,7 +2405,9 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps case isLeader = <-lch: if isLeader { - if mset != nil && n != nil && sendSnapshot { + if mset != nil && n != nil && sendSnapshot && !isRecovering { + // If we *are* recovering at the time then this will get done when the apply queue + // handles the nil guard to show the catchup ended. n.SendSnapshot(mset.stateSnapshot()) sendSnapshot = false } @@ -2841,6 +2874,19 @@ func (js *jetStream) applyStreamEntries(mset *stream, ce *CommittedEntry, isReco // Process the actual message here. if err := mset.processJetStreamMsg(subject, reply, hdr, msg, lseq, ts); err != nil { + if err == errLastSeqMismatch { + var state StreamState + mset.store.FastState(&state) + // If we have no msgs and the other side is delivering us a sequence past where we + // should be reset. This is possible if the other side has a stale snapshot and no longer + // has those messages. So compact and retry to reset. + if state.Msgs == 0 { + mset.store.Compact(lseq + 1) + // Retry + err = mset.processJetStreamMsg(subject, reply, hdr, msg, lseq, ts) + } + } + // Only return in place if we are going to reset our stream or we are out of space, or we are closed. if isClusterResetErr(err) || isOutOfSpaceErr(err) || err == errStreamClosed { return err @@ -4086,6 +4132,7 @@ func (js *jetStream) processConsumerRemoval(ca *consumerAssignment) { // Make sure this removal is for what we have, otherwise ignore. if ca.Group != nil && oca.Group != nil && ca.Group.Name == oca.Group.Name { needDelete = true + oca.deleted = true delete(sa.consumers, ca.Name) } } @@ -4366,7 +4413,6 @@ func (js *jetStream) processClusterDeleteConsumer(ca *consumerAssignment, isMemb recovering := ca.recovering js.mu.RUnlock() - stopped := false var resp = JSApiConsumerDeleteResponse{ApiResponse: ApiResponse{Type: JSApiConsumerDeleteResponseType}} var err error var acc *Account @@ -4376,9 +4422,13 @@ func (js *jetStream) processClusterDeleteConsumer(ca *consumerAssignment, isMemb if mset, _ := acc.lookupStream(ca.Stream); mset != nil { if o := mset.lookupConsumer(ca.Name); o != nil { err = o.stopWithFlags(true, false, true, wasLeader) - stopped = true } } + } else if ca.Group != nil { + // We have a missing account, see if we can cleanup. + if sacc := s.SystemAccount(); sacc != nil { + os.RemoveAll(filepath.Join(js.config.StoreDir, sacc.GetName(), defaultStoreDirName, ca.Group.Name)) + } } // Always delete the node if present. @@ -4386,15 +4436,6 @@ func (js *jetStream) processClusterDeleteConsumer(ca *consumerAssignment, isMemb node.Delete() } - // This is a stop gap cleanup in case - // 1) the account does not exist (and mset consumer couldn't be stopped) and/or - // 2) node was nil (and couldn't be deleted) - if !stopped || node == nil { - if sacc := s.SystemAccount(); sacc != nil { - os.RemoveAll(filepath.Join(js.config.StoreDir, sacc.GetName(), defaultStoreDirName, ca.Group.Name)) - } - } - if !wasLeader || ca.Reply == _EMPTY_ { if !(offline && isMetaLeader) { return @@ -4595,8 +4636,8 @@ func (js *jetStream) monitorConsumer(o *consumer, ca *consumerAssignment) { if !bytes.Equal(hash[:], lastSnap) || ne >= compactNumMin || nb >= compactSizeMin { if err := n.InstallSnapshot(snap); err == nil { lastSnap, lastSnapTime = hash[:], time.Now() - } else if err != errNoSnapAvailable && err != errNodeClosed { - s.Warnf("Failed to install snapshot for '%s > %s > %s' [%s]: %v", o.acc.Name, ca.Stream, ca.Name, n.Group(), err) + } else if err != errNoSnapAvailable && err != errNodeClosed && err != errCatchupsRunning { + s.RateLimitWarnf("Failed to install snapshot for '%s > %s > %s' [%s]: %v", o.acc.Name, ca.Stream, ca.Name, n.Group(), err) } } } @@ -4658,16 +4699,6 @@ func (js *jetStream) monitorConsumer(o *consumer, ca *consumerAssignment) { js.setConsumerAssignmentRecovering(ca) } - // Synchronize everyone to our state. - if isLeader && n != nil { - // Only send out if we have state. - if _, _, applied := n.Progress(); applied > 0 { - if snap, err := o.store.EncodedState(); err == nil { - n.SendSnapshot(snap) - } - } - } - // Process the change. if err := js.processConsumerLeaderChange(o, isLeader); err == nil && isLeader { doSnapshot(true) @@ -7789,6 +7820,7 @@ func (mset *stream) processSnapshot(snap *StreamReplicatedState) (e error) { mset.store.FastState(&state) mset.setCLFS(snap.Failed) sreq := mset.calculateSyncRequest(&state, snap) + s, js, subject, n := mset.srv, mset.js, mset.sa.Sync, mset.node qname := fmt.Sprintf("[ACC:%s] stream '%s' snapshot", mset.acc.Name, mset.cfg.Name) mset.mu.Unlock() diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_events.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_events.go index ab7c61915..7ea14ef85 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_events.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_events.go @@ -18,7 +18,7 @@ import ( "time" ) -func (s *Server) publishAdvisory(acc *Account, subject string, adv interface{}) { +func (s *Server) publishAdvisory(acc *Account, subject string, adv any) { if acc == nil { acc = s.SystemAccount() if acc == nil { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go index 8d324d01f..6abf0f005 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go @@ -22,6 +22,7 @@ import ( "time" "github.com/nats-io/nats-server/v2/server/avl" + "github.com/nats-io/nats-server/v2/server/stree" ) // TODO(dlc) - This is a fairly simplistic approach but should do for now. @@ -30,7 +31,7 @@ type memStore struct { cfg StreamConfig state StreamState msgs map[uint64]*StoreMsg - fss map[string]*SimpleState + fss *stree.SubjectTree[SimpleState] dmap avl.SequenceSet maxp int64 scb StorageUpdateHandler @@ -48,7 +49,7 @@ func newMemStore(cfg *StreamConfig) (*memStore, error) { } ms := &memStore{ msgs: make(map[uint64]*StoreMsg), - fss: make(map[string]*SimpleState), + fss: stree.NewSubjectTree[SimpleState](), maxp: cfg.MaxMsgsPer, cfg: *cfg, } @@ -88,11 +89,12 @@ func (ms *memStore) UpdateConfig(cfg *StreamConfig) error { // If the value is smaller we need to enforce that. if ms.maxp != 0 && ms.maxp < maxp { lm := uint64(ms.maxp) - for subj, ss := range ms.fss { + ms.fss.Iter(func(subj []byte, ss *SimpleState) bool { if ss.Msgs > lm { - ms.enforcePerSubjectLimit(subj, ss) + ms.enforcePerSubjectLimit(bytesToString(subj), ss) } - } + return true + }) } ms.mu.Unlock() @@ -113,7 +115,8 @@ func (ms *memStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts int var ss *SimpleState var asl bool if len(subj) > 0 { - if ss = ms.fss[subj]; ss != nil { + var ok bool + if ss, ok = ms.fss.Find(stringToBytes(subj)); ok { asl = ms.maxp > 0 && ss.Msgs >= uint64(ms.maxp) } } @@ -191,7 +194,7 @@ func (ms *memStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts int ms.enforcePerSubjectLimit(subj, ss) } } else { - ms.fss[subj] = &SimpleState{Msgs: 1, First: seq, Last: seq} + ms.fss.Insert([]byte(subj), SimpleState{Msgs: 1, First: seq, Last: seq}) } } @@ -363,14 +366,17 @@ func (ms *memStore) filteredStateLocked(sseq uint64, filter string, lastPerSubje return ss } - isAll := filter == _EMPTY_ || filter == fwcs + if filter == _EMPTY_ { + filter = fwcs + } + isAll := filter == fwcs // First check if we can optimize this part. // This means we want all and the starting sequence was before this block. if isAll && sseq <= ms.state.FirstSeq { total := ms.state.Msgs if lastPerSubject { - total = uint64(len(ms.fss)) + total = uint64(ms.fss.Size()) } return SimpleState{ Msgs: total, @@ -415,21 +421,20 @@ func (ms *memStore) filteredStateLocked(sseq uint64, filter string, lastPerSubje var havePartial bool // We will track start and end sequences as we go. - for subj, fss := range ms.fss { - if isMatch(subj) { - if fss.firstNeedsUpdate { - ms.recalculateFirstForSubj(subj, fss.First, fss) - } - if sseq <= fss.First { - update(fss) - } else if sseq <= fss.Last { - // We matched but its a partial. - havePartial = true - // Don't break here, we will update to keep tracking last. - update(fss) - } + ms.fss.Match(stringToBytes(filter), func(subj []byte, fss *SimpleState) { + subjs := bytesToString(subj) + if fss.firstNeedsUpdate { + ms.recalculateFirstForSubj(subjs, fss.First, fss) } - } + if sseq <= fss.First { + update(fss) + } else if sseq <= fss.Last { + // We matched but its a partial. + havePartial = true + // Don't break here, we will update to keep tracking last. + update(fss) + } + }) // If we did not encounter any partials we can return here. if !havePartial { @@ -476,7 +481,7 @@ func (ms *memStore) filteredStateLocked(sseq uint64, filter string, lastPerSubje for seq := ms.state.FirstSeq; seq < first; seq++ { if sm, ok := ms.msgs[seq]; ok && !seen[sm.subj] && isMatch(sm.subj) { if lastPerSubject { - tss = ms.fss[sm.subj] + tss, _ = ms.fss.Find(stringToBytes(sm.subj)) } // If we are last per subject, make sure to only adjust if all messages are before our first. if tss == nil || tss.Last < first { @@ -515,26 +520,29 @@ func (ms *memStore) SubjectsState(subject string) map[string]SimpleState { ms.mu.RLock() defer ms.mu.RUnlock() - if len(ms.fss) == 0 { + if ms.fss.Size() == 0 { return nil } - fss := make(map[string]SimpleState) - for subj, ss := range ms.fss { - if subject == _EMPTY_ || subject == fwcs || subjectIsSubsetMatch(subj, subject) { - if ss.firstNeedsUpdate { - ms.recalculateFirstForSubj(subj, ss.First, ss) - } - oss := fss[subj] - if oss.First == 0 { // New - fss[subj] = *ss - } else { - // Merge here. - oss.Last, oss.Msgs = ss.Last, oss.Msgs+ss.Msgs - fss[subj] = oss - } - } + if subject == _EMPTY_ { + subject = fwcs } + + fss := make(map[string]SimpleState) + ms.fss.Match(stringToBytes(subject), func(subj []byte, ss *SimpleState) { + subjs := string(subj) + if ss.firstNeedsUpdate { + ms.recalculateFirstForSubj(subjs, ss.First, ss) + } + oss := fss[subjs] + if oss.First == 0 { // New + fss[subjs] = *ss + } else { + // Merge here. + oss.Last, oss.Msgs = ss.Last, oss.Msgs+ss.Msgs + fss[subjs] = oss + } + }) return fss } @@ -543,7 +551,7 @@ func (ms *memStore) SubjectsTotals(filterSubject string) map[string]uint64 { ms.mu.RLock() defer ms.mu.RUnlock() - if len(ms.fss) == 0 { + if ms.fss.Size() == 0 { return nil } @@ -553,15 +561,16 @@ func (ms *memStore) SubjectsTotals(filterSubject string) map[string]uint64 { isAll := filterSubject == _EMPTY_ || filterSubject == fwcs fst := make(map[string]uint64) - for subj, ss := range ms.fss { + ms.fss.Match(stringToBytes(filterSubject), func(subj []byte, ss *SimpleState) { + subjs := string(subj) if isAll { - fst[subj] = ss.Msgs + fst[subjs] = ss.Msgs } else { - if tts := tokenizeSubjectIntoSlice(tsa[:0], subj); isSubsetMatchTokenized(tts, fts) { - fst[subj] = ss.Msgs + if tts := tokenizeSubjectIntoSlice(tsa[:0], subjs); isSubsetMatchTokenized(tts, fts) { + fst[subjs] = ss.Msgs } } - } + }) return fst } @@ -755,7 +764,7 @@ func (ms *memStore) purge(fseq uint64) (uint64, error) { ms.state.Bytes = 0 ms.state.Msgs = 0 ms.msgs = make(map[uint64]*StoreMsg) - ms.fss = make(map[string]*SimpleState) + ms.fss = stree.NewSubjectTree[SimpleState]() ms.mu.Unlock() if cb != nil { @@ -846,7 +855,7 @@ func (ms *memStore) reset() error { ms.state.Bytes = 0 // Reset msgs and fss. ms.msgs = make(map[uint64]*StoreMsg) - ms.fss = make(map[string]*SimpleState) + ms.fss = stree.NewSubjectTree[SimpleState]() ms.mu.Unlock() @@ -950,7 +959,8 @@ func (ms *memStore) LoadLastMsg(subject string, smp *StoreMsg) (*StoreMsg, error if subject == _EMPTY_ || subject == fwcs { sm, ok = ms.msgs[ms.state.LastSeq] } else if subjectIsLiteral(subject) { - if ss := ms.fss[subject]; ss != nil && ss.Msgs > 0 { + var ss *SimpleState + if ss, ok = ms.fss.Find(stringToBytes(subject)); ok && ss.Msgs > 0 { sm, ok = ms.msgs[ss.Last] } } else if ss := ms.filteredStateLocked(1, subject, true); ss.Msgs > 0 { @@ -982,12 +992,15 @@ func (ms *memStore) LoadNextMsg(filter string, wc bool, start uint64, smp *Store return nil, ms.state.LastSeq, ErrStoreEOF } - isAll := filter == _EMPTY_ || filter == fwcs + if filter == _EMPTY_ { + filter = fwcs + } + isAll := filter == fwcs // Skip scan of ms.fss is number of messages in the block are less than // 1/2 the number of subjects in ms.fss. Or we have a wc and lots of fss entries. const linearScanMaxFSS = 256 - doLinearScan := isAll || 2*int(ms.state.LastSeq-start) < len(ms.fss) || (wc && len(ms.fss) > linearScanMaxFSS) + doLinearScan := isAll || 2*int(ms.state.LastSeq-start) < ms.fss.Size() || (wc && ms.fss.Size() > linearScanMaxFSS) // Initial setup. fseq, lseq := start, ms.state.LastSeq @@ -996,16 +1009,14 @@ func (ms *memStore) LoadNextMsg(filter string, wc bool, start uint64, smp *Store subs := []string{filter} if wc || isAll { subs = subs[:0] - for fsubj := range ms.fss { - if isAll || subjectIsSubsetMatch(fsubj, filter) { - subs = append(subs, fsubj) - } - } + ms.fss.Match(stringToBytes(filter), func(subj []byte, val *SimpleState) { + subs = append(subs, string(subj)) + }) } fseq, lseq = ms.state.LastSeq, uint64(0) for _, subj := range subs { - ss := ms.fss[subj] - if ss == nil { + ss, ok := ms.fss.Find(stringToBytes(subj)) + if !ok { continue } if ss.firstNeedsUpdate { @@ -1093,12 +1104,12 @@ func (ms *memStore) updateFirstSeq(seq uint64) { // Remove a seq from the fss and select new first. // Lock should be held. func (ms *memStore) removeSeqPerSubject(subj string, seq uint64) { - ss := ms.fss[subj] - if ss == nil { + ss, ok := ms.fss.Find(stringToBytes(subj)) + if !ok { return } if ss.Msgs == 1 { - delete(ms.fss, subj) + ms.fss.Delete(stringToBytes(subj)) return } ss.Msgs-- @@ -1198,7 +1209,7 @@ func (ms *memStore) FastState(state *StreamState) { } } state.Consumers = ms.consumers - state.NumSubjects = len(ms.fss) + state.NumSubjects = ms.fss.Size() ms.mu.RUnlock() } @@ -1208,7 +1219,7 @@ func (ms *memStore) State() StreamState { state := ms.state state.Consumers = ms.consumers - state.NumSubjects = len(ms.fss) + state.NumSubjects = ms.fss.Size() state.Deleted = nil // Calculate interior delete details. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go index e82ddb293..793d57d27 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go @@ -1439,26 +1439,31 @@ func (s *Server) mqttCreateAccountSessionManager(acc *Account, quitCh chan struc } } + // Opportunistically delete the old (legacy) consumer, from v2.10.10 and + // before. Ignore any errors that might arise. + rmLegacyDurName := mqttRetainedMsgsStreamName + "_" + jsa.id + jsa.deleteConsumer(mqttRetainedMsgsStreamName, rmLegacyDurName) + // Using ephemeral consumer is too risky because if this server were to be // disconnected from the rest for few seconds, then the leader would remove // the consumer, so even after a reconnect, we would no longer receive - // retained messages. Delete any existing durable that we have for that - // and recreate here. - // The name for the durable is $MQTT_rmsgs_ (which is jsa.id) - rmDurName := mqttRetainedMsgsStreamName + "_" + jsa.id - // If error other than "not found" then fail, otherwise proceed with creating - // the durable consumer. - if _, err := jsa.deleteConsumer(mqttRetainedMsgsStreamName, rmDurName); isErrorOtherThan(err, JSConsumerNotFoundErr) { - return nil, err - } + // retained messages. + // + // So we use a durable consumer, and create a new one each time we start. + // The old one should expire and get deleted due to inactivity. The name for + // the durable is $MQTT_rmsgs_{uuid}_{server-name}, the server name is just + // for readability. + rmDurName := mqttRetainedMsgsStreamName + "_" + nuid.Next() + "_" + s.String() + ccfg := &CreateConsumerRequest{ Stream: mqttRetainedMsgsStreamName, Config: ConsumerConfig{ - Durable: rmDurName, - FilterSubject: mqttRetainedMsgsStreamSubject + ">", - DeliverSubject: rmsubj, - ReplayPolicy: ReplayInstant, - AckPolicy: AckNone, + Durable: rmDurName, + FilterSubject: mqttRetainedMsgsStreamSubject + ">", + DeliverSubject: rmsubj, + ReplayPolicy: ReplayInstant, + AckPolicy: AckNone, + InactiveThreshold: 5 * time.Minute, }, } if _, err := jsa.createConsumer(ccfg); err != nil { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/opts.go b/vendor/github.com/nats-io/nats-server/v2/server/opts.go index 8925ef6df..721d6be84 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/opts.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/opts.go @@ -80,6 +80,8 @@ type ClusterOpts struct { PoolSize int `json:"-"` PinnedAccounts []string `json:"-"` Compression CompressionOpts `json:"-"` + PingInterval time.Duration `json:"-"` + MaxPingsOut int `json:"-"` // Not exported (used in tests) resolver netResolver @@ -1755,6 +1757,13 @@ func parseCluster(v interface{}, opts *Options, errors *[]error, warnings *[]err *errors = append(*errors, err) continue } + case "ping_interval": + opts.Cluster.PingInterval = parseDuration("ping_interval", tk, mv, errors, warnings) + if opts.Cluster.PingInterval > routeMaxPingInterval { + *warnings = append(*warnings, &configErr{tk, fmt.Sprintf("Cluster 'ping_interval' will reset to %v which is the max for routes", routeMaxPingInterval)}) + } + case "ping_max": + opts.Cluster.MaxPingsOut = int(mv.(int64)) default: if !tk.IsUsedVariable() { err := &unknownConfigFieldErr{ diff --git a/vendor/github.com/nats-io/nats-server/v2/server/raft.go b/vendor/github.com/nats-io/nats-server/v2/server/raft.go index 6990812c4..f041fb089 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/raft.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/raft.go @@ -154,11 +154,12 @@ type raft struct { llqrt time.Time // Last quorum lost time lsut time.Time // Last scale-up time - term uint64 // The current vote term - pterm uint64 // Previous term from the last snapshot - pindex uint64 // Previous index from the last snapshot - commit uint64 // Sequence number of the most recent commit - applied uint64 // Sequence number of the most recently applied commit + term uint64 // The current vote term + pterm uint64 // Previous term from the last snapshot + pindex uint64 // Previous index from the last snapshot + commit uint64 // Sequence number of the most recent commit + applied uint64 // Sequence number of the most recently applied commit + hcbehind bool // Were we falling behind at the last health check? (see: isCurrent) leader string // The ID of the leader vote string // Our current vote state @@ -1052,7 +1053,11 @@ func (n *raft) InstallSnapshot(data []byte) error { sn := fmt.Sprintf(snapFileT, snap.lastTerm, snap.lastIndex) sfile := filepath.Join(snapDir, sn) - if err := os.WriteFile(sfile, n.encodeSnapshot(snap), defaultFilePerms); err != nil { + <-dios + err := os.WriteFile(sfile, n.encodeSnapshot(snap), defaultFilePerms) + dios <- struct{}{} + + if err != nil { n.Unlock() // We could set write err here, but if this is a temporary situation, too many open files etc. // we want to retry and snapshots are not fatal. @@ -1187,7 +1192,11 @@ func (n *raft) loadLastSnapshot() (*snapshot, error) { if n.snapfile == _EMPTY_ { return nil, errNoSnapAvailable } + + <-dios buf, err := os.ReadFile(n.snapfile) + dios <- struct{}{} + if err != nil { n.warn("Error reading snapshot: %v", err) os.Remove(n.snapfile) @@ -1269,8 +1278,18 @@ func (n *raft) isCurrent(includeForwardProgress bool) bool { return false } + // If we were previously logging about falling behind, also log when the problem + // was cleared. + clearBehindState := func() { + if n.hcbehind { + n.warn("Health check OK, no longer falling behind") + n.hcbehind = false + } + } + // Make sure we are the leader or we know we have heard from the leader recently. if n.State() == Leader { + clearBehindState() return true } @@ -1294,6 +1313,7 @@ func (n *raft) isCurrent(includeForwardProgress bool) bool { if n.commit == n.applied { // At this point if we are current, we can return saying so. + clearBehindState() return true } else if !includeForwardProgress { // Otherwise, if we aren't allowed to include forward progress @@ -1311,11 +1331,13 @@ func (n *raft) isCurrent(includeForwardProgress bool) bool { n.Lock() if n.commit-n.applied < startDelta { // The gap is getting smaller, so we're making forward progress. + clearBehindState() return true } } } + n.hcbehind = true n.warn("Falling behind in health check, commit %d != applied %d", n.commit, n.applied) return false } @@ -3678,14 +3700,22 @@ func writePeerState(sd string, ps *peerState) error { if _, err := os.Stat(psf); err != nil && !os.IsNotExist(err) { return err } - if err := os.WriteFile(psf, encodePeerState(ps), defaultFilePerms); err != nil { + + <-dios + err := os.WriteFile(psf, encodePeerState(ps), defaultFilePerms) + dios <- struct{}{} + + if err != nil { return err } return nil } func readPeerState(sd string) (ps *peerState, err error) { + <-dios buf, err := os.ReadFile(filepath.Join(sd, peerStateFile)) + dios <- struct{}{} + if err != nil { return nil, err } @@ -3698,7 +3728,10 @@ const termVoteLen = idLen + 8 // readTermVote will read the largest term and who we voted from to stable storage. // Lock should be held. func (n *raft) readTermVote() (term uint64, voted string, err error) { + <-dios buf, err := os.ReadFile(filepath.Join(n.sd, termVoteFile)) + dios <- struct{}{} + if err != nil { return 0, noVote, err } @@ -3920,6 +3953,10 @@ func (n *raft) processVoteRequest(vr *voteRequest) error { n.resetElect(randCampaignTimeout()) } } + + // Term might have changed, make sure response has the most current + vresp.term = n.term + n.Unlock() n.sendReply(vr.reply, vresp.encode()) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/route.go b/vendor/github.com/nats-io/nats-server/v2/server/route.go index e73f1bdf7..f8a8623d6 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/route.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/route.go @@ -1780,7 +1780,15 @@ func (s *Server) createRoute(conn net.Conn, rURL *url.URL, accName string) *clie // the connection as stale based on the ping interval and max out values, // but without actually sending pings. if compressionConfigured { - c.ping.tmr = time.AfterFunc(opts.PingInterval*time.Duration(opts.MaxPingsOut+1), func() { + pingInterval := opts.PingInterval + pingMax := opts.MaxPingsOut + if opts.Cluster.PingInterval > 0 { + pingInterval = opts.Cluster.PingInterval + } + if opts.Cluster.MaxPingsOut > 0 { + pingMax = opts.MaxPingsOut + } + c.ping.tmr = time.AfterFunc(pingInterval*time.Duration(pingMax+1), func() { c.mu.Lock() c.Debugf("Stale Client Connection - Closing") c.enqueueProto([]byte(fmt.Sprintf(errProto, "Stale Connection"))) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stream.go b/vendor/github.com/nats-io/nats-server/v2/server/stream.go index f032f285b..b2bef9a86 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stream.go @@ -637,9 +637,9 @@ func (a *Account) addStreamWithAssignment(config *StreamConfig, fsConfig *FileSt mset.store.FastState(&state) // Possible race with consumer.setLeader during recovery. - mset.mu.Lock() + mset.mu.RLock() mset.lseq = state.LastSeq - mset.mu.Unlock() + mset.mu.RUnlock() // If no msgs (new stream), set dedupe state loaded to true. if state.Msgs == 0 { @@ -5763,7 +5763,7 @@ func (a *Account) RestoreStream(ncfg *StreamConfig, r io.Reader) (*stream, error return mset, nil } -// This is to check for dangling messages on interest retention streams. +// This is to check for dangling messages on interest retention streams. Only called on account enable. // Issue https://github.com/nats-io/nats-server/issues/3612 func (mset *stream) checkForOrphanMsgs() { mset.mu.RLock() @@ -5771,9 +5771,23 @@ func (mset *stream) checkForOrphanMsgs() { for _, o := range mset.consumers { consumers = append(consumers, o) } + accName, stream := mset.acc.Name, mset.cfg.Name + + var ss StreamState + mset.store.FastState(&ss) mset.mu.RUnlock() + for _, o := range consumers { - o.checkStateForInterestStream() + if err := o.checkStateForInterestStream(); err == errAckFloorHigherThanLastSeq { + o.mu.RLock() + s, consumer := o.srv, o.name + state, _ := o.store.State() + asflr := state.AckFloor.Stream + o.mu.RUnlock() + // Warn about stream state vs our ack floor. + s.RateLimitWarnf("Detected consumer '%s > %s > %s' ack floor %d is ahead of stream's last sequence %d", + accName, stream, consumer, asflr, ss.LastSeq) + } } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/dump.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/dump.go new file mode 100644 index 000000000..79b6fd99f --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/dump.go @@ -0,0 +1,68 @@ +// Copyright 2024 The NATS Authors +// 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. + +package stree + +import ( + "fmt" + "io" + "strings" +) + +// For dumping out a text representation of a tree. +func (t *SubjectTree[T]) Dump(w io.Writer) { + t.dump(w, t.root, 0) + fmt.Fprintln(w) +} + +// Will dump out a node. +func (t *SubjectTree[T]) dump(w io.Writer, n node, depth int) { + if n == nil { + fmt.Fprintf(w, "EMPTY\n") + return + } + if n.isLeaf() { + leaf := n.(*leaf[T]) + fmt.Fprintf(w, "%s LEAF: Suffix: %q Value: %+v\n", dumpPre(depth), leaf.suffix, leaf.value) + n = nil + } else { + // We are a node type here, grab meta portion. + bn := n.base() + fmt.Fprintf(w, "%s %s Prefix: %q\n", dumpPre(depth), n.kind(), bn.prefix[:bn.prefixLen]) + depth++ + n.iter(func(n node) bool { + t.dump(w, n, depth) + return true + }) + } +} + +// For individual node/leaf dumps. +func (n *leaf[T]) kind() string { return "LEAF" } +func (n *node4) kind() string { return "NODE4" } +func (n *node16) kind() string { return "NODE16" } +func (n *node256) kind() string { return "NODE256" } + +// Calculates the indendation, etc. +func dumpPre(depth int) string { + if depth == 0 { + return "-- " + } else { + var b strings.Builder + for i := 0; i < depth; i++ { + b.WriteString(" ") + } + b.WriteString("|__ ") + return b.String() + } +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go new file mode 100644 index 000000000..839450f2e --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go @@ -0,0 +1,50 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +import ( + "bytes" +) + +// Leaf node +type leaf[T any] struct { + // This could be the whole subject, but most likely just the suffix portion. + // We will only store the suffix here and assume all prior prefix paths have + // been checked once we arrive at this leafnode. + suffix []byte + value T +} + +func newLeaf[T any](suffix []byte, value T) *leaf[T] { + return &leaf[T]{copyBytes(suffix), value} +} + +func (n *leaf[T]) isLeaf() bool { return true } +func (n *leaf[T]) base() *meta { return nil } +func (n *leaf[T]) match(subject []byte) bool { return bytes.Equal(subject, n.suffix) } +func (n *leaf[T]) setSuffix(suffix []byte) { n.suffix = copyBytes(suffix) } +func (n *leaf[T]) isFull() bool { return true } +func (n *leaf[T]) matchParts(parts [][]byte) ([][]byte, bool) { return matchParts(parts, n.suffix) } +func (n *leaf[T]) iter(f func(node) bool) {} +func (n *leaf[T]) children() []node { return nil } +func (n *leaf[T]) numChildren() uint16 { return 0 } +func (n *leaf[T]) path() []byte { return n.suffix } + +// Not applicable to leafs and should not be called, so panic if we do. +func (n *leaf[T]) setPrefix(pre []byte) { panic("setPrefix called on leaf") } +func (n *leaf[T]) addChild(_ byte, _ node) { panic("addChild called on leaf") } +func (n *leaf[T]) findChild(_ byte) *node { panic("findChild called on leaf") } +func (n *leaf[T]) grow() node { panic("grow called on leaf") } +func (n *leaf[T]) deleteChild(_ byte) { panic("deleteChild called on leaf") } +func (n *leaf[T]) shrink() node { panic("shrink called on leaf") } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go new file mode 100644 index 000000000..0afecfab9 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go @@ -0,0 +1,45 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +// Internal node interface. +type node interface { + isLeaf() bool + base() *meta + setPrefix(pre []byte) + addChild(c byte, n node) + findChild(c byte) *node + deleteChild(c byte) + isFull() bool + grow() node + shrink() node + matchParts(parts [][]byte) ([][]byte, bool) + kind() string + iter(f func(node) bool) + children() []node + numChildren() uint16 + path() []byte +} + +// Maximum prefix len +// We expect the most savings to come from long shared prefixes. +// This also makes the meta base layer exactly 64 bytes, a normal L1 cache line. +const maxPrefixLen = 60 + +// 64 bytes total - an L1 cache line. +type meta struct { + prefix [maxPrefixLen]byte + prefixLen uint16 + size uint16 +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go new file mode 100644 index 000000000..b61f286dc --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go @@ -0,0 +1,121 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +// Node with 16 children +type node16 struct { + meta + child [16]node + key [16]byte +} + +func newNode16(prefix []byte) *node16 { + nn := &node16{} + nn.setPrefix(prefix) + return nn +} + +func (n *node16) isLeaf() bool { return false } +func (n *node16) base() *meta { return &n.meta } + +func (n *node16) setPrefix(pre []byte) { + n.prefixLen = uint16(min(len(pre), maxPrefixLen)) + for i := uint16(0); i < n.prefixLen; i++ { + n.prefix[i] = pre[i] + } +} + +// Currently we do not keep node16 sorted or use bitfields for traversal so just add to the end. +// TODO(dlc) - We should revisit here with more detailed benchmarks. +func (n *node16) addChild(c byte, nn node) { + if n.size >= 16 { + panic("node16 full!") + } + n.key[n.size] = c + n.child[n.size] = nn + n.size++ +} + +func (n *node16) numChildren() uint16 { return n.size } +func (n *node16) path() []byte { return n.prefix[:n.prefixLen] } + +func (n *node16) findChild(c byte) *node { + for i := uint16(0); i < n.size; i++ { + if n.key[i] == c { + return &n.child[i] + } + } + return nil +} + +func (n *node16) isFull() bool { return n.size >= 16 } + +func (n *node16) grow() node { + nn := newNode256(n.prefix[:n.prefixLen]) + for i := 0; i < 16; i++ { + nn.addChild(n.key[i], n.child[i]) + } + return nn +} + +// Deletes a child from the node. +func (n *node16) deleteChild(c byte) { + for i, last := uint16(0), n.size-1; i < n.size; i++ { + if n.key[i] == c { + // Unsorted so just swap in last one here, else nil if last. + if i < last { + n.key[i] = n.key[last] + n.child[i] = n.child[last] + n.key[last] = 0 + n.child[last] = nil + } else { + n.key[i] = 0 + n.child[i] = nil + } + n.size-- + return + } + } +} + +// Shrink if needed and return new node, otherwise return nil. +func (n *node16) shrink() node { + if n.size > 4 { + return nil + } + nn := newNode4(nil) + for i := uint16(0); i < n.size; i++ { + nn.addChild(n.key[i], n.child[i]) + } + return nn +} + +// Will match parts against our prefix.no +func (n *node16) matchParts(parts [][]byte) ([][]byte, bool) { + return matchParts(parts, n.prefix[:n.prefixLen]) +} + +// Iterate over all children calling func f. +func (n *node16) iter(f func(node) bool) { + for i := uint16(0); i < n.size; i++ { + if !f(n.child[i]) { + return + } + } +} + +// Return our children as a slice. +func (n *node16) children() []node { + return n.child[:n.size] +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node256.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node256.go new file mode 100644 index 000000000..c7bcd3362 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node256.go @@ -0,0 +1,97 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +// Node with 256 children +type node256 struct { + meta + child [256]node +} + +func newNode256(prefix []byte) *node256 { + nn := &node256{} + nn.setPrefix(prefix) + return nn +} + +func (n *node256) isLeaf() bool { return false } +func (n *node256) base() *meta { return &n.meta } + +func (n *node256) setPrefix(pre []byte) { + n.prefixLen = uint16(min(len(pre), maxPrefixLen)) + for i := uint16(0); i < n.prefixLen; i++ { + n.prefix[i] = pre[i] + } +} + +func (n *node256) addChild(c byte, nn node) { + n.child[c] = nn + n.size++ +} + +func (n *node256) numChildren() uint16 { return n.size } +func (n *node256) path() []byte { return n.prefix[:n.prefixLen] } + +func (n *node256) findChild(c byte) *node { + if n.child[c] != nil { + return &n.child[c] + } + return nil +} + +func (n *node256) isFull() bool { return false } +func (n *node256) grow() node { panic("grow can not be called on node256") } + +// Deletes a child from the node. +func (n *node256) deleteChild(c byte) { + if n.child[c] != nil { + n.child[c] = nil + n.size-- + } +} + +// Shrink if needed and return new node, otherwise return nil. +func (n *node256) shrink() node { + if n.size > 16 { + return nil + } + nn := newNode16(nil) + for c, child := range n.child { + if child != nil { + nn.addChild(byte(c), n.child[c]) + } + } + return nn +} + +// Will match parts against our prefix. +func (n *node256) matchParts(parts [][]byte) ([][]byte, bool) { + return matchParts(parts, n.prefix[:n.prefixLen]) +} + +// Iterate over all children calling func f. +func (n *node256) iter(f func(node) bool) { + for i := 0; i < 256; i++ { + if n.child[i] != nil { + if !f(n.child[i]) { + return + } + } + } +} + +// Return our children as a slice. +func (n *node256) children() []node { + return n.child[:256] +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go new file mode 100644 index 000000000..195299e26 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go @@ -0,0 +1,116 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +// Node with 4 children +type node4 struct { + meta + child [4]node + key [4]byte +} + +func newNode4(prefix []byte) *node4 { + nn := &node4{} + nn.setPrefix(prefix) + return nn +} + +func (n *node4) isLeaf() bool { return false } +func (n *node4) base() *meta { return &n.meta } + +func (n *node4) setPrefix(pre []byte) { + n.prefixLen = uint16(min(len(pre), maxPrefixLen)) + for i := uint16(0); i < n.prefixLen; i++ { + n.prefix[i] = pre[i] + } +} + +// Currently we do not need to keep sorted for traversal so just add to the end. +func (n *node4) addChild(c byte, nn node) { + if n.size >= 4 { + panic("node4 full!") + } + n.key[n.size] = c + n.child[n.size] = nn + n.size++ +} + +func (n *node4) numChildren() uint16 { return n.size } +func (n *node4) path() []byte { return n.prefix[:n.prefixLen] } + +func (n *node4) findChild(c byte) *node { + for i := uint16(0); i < n.size; i++ { + if n.key[i] == c { + return &n.child[i] + } + } + return nil +} + +func (n *node4) isFull() bool { return n.size >= 4 } + +func (n *node4) grow() node { + nn := newNode16(n.prefix[:n.prefixLen]) + for i := 0; i < 4; i++ { + nn.addChild(n.key[i], n.child[i]) + } + return nn +} + +// Deletes a child from the node. +func (n *node4) deleteChild(c byte) { + for i, last := uint16(0), n.size-1; i < n.size; i++ { + if n.key[i] == c { + // Unsorted so just swap in last one here, else nil if last. + if i < last { + n.key[i] = n.key[last] + n.child[i] = n.child[last] + n.key[last] = 0 + n.child[last] = nil + } else { + n.key[i] = 0 + n.child[i] = nil + } + n.size-- + return + } + } +} + +// Shrink if needed and return new node, otherwise return nil. +func (n *node4) shrink() node { + if n.size == 1 { + return n.child[0] + } + return nil +} + +// Will match parts against our prefix. +func (n *node4) matchParts(parts [][]byte) ([][]byte, bool) { + return matchParts(parts, n.prefix[:n.prefixLen]) +} + +// Iterate over all children calling func f. +func (n *node4) iter(f func(node) bool) { + for i := uint16(0); i < n.size; i++ { + if !f(n.child[i]) { + return + } + } +} + +// Return our children as a slice. +func (n *node4) children() []node { + return n.child[:n.size] +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go new file mode 100644 index 000000000..71783ee11 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go @@ -0,0 +1,134 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +import ( + "bytes" +) + +// genParts will break a filter subject up into parts. +// We need to break this up into chunks based on wildcards, either pwc '*' or fwc '>'. +// We do not care about other tokens per se, just parts that are separated by wildcards with an optional end fwc. +func genParts(filter []byte, parts [][]byte) [][]byte { + var start int + for i, e := 0, len(filter)-1; i < len(filter); i++ { + if filter[i] == tsep { + // See if next token is pwc. Either internal or end pwc. + if i < e && filter[i+1] == pwc && (i+2 <= e && filter[i+2] == tsep || i+1 == e) { + if i > start { + parts = append(parts, filter[start:i+1]) + } + parts = append(parts, filter[i+1:i+2]) + i++ // Skip pwc + if i+2 <= e { + i++ // Skip next tsep from next part too. + } + start = i + 1 + } else if i < e && filter[i+1] == fwc && i+1 == e { + // We have a fwc + if i > start { + parts = append(parts, filter[start:i+1]) + } + parts = append(parts, filter[i+1:i+2]) + i++ // Skip fwc + start = i + 1 + } + } else if filter[i] == pwc || filter[i] == fwc { + // We start with a pwc or fwc. + parts = append(parts, filter[i:i+1]) + if i+1 <= e { + i++ // Skip next tsep from next part too. + } + start = i + 1 + } + } + if start < len(filter) { + // Check to see if we need to eat a leading tsep. + if filter[start] == tsep { + start++ + } + parts = append(parts, filter[start:]) + } + return parts +} + +// Match our parts against a fragment, which could be prefix for nodes or a suffix for leafs. +func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { + if len(frag) == 0 { + return parts, true + } + + var si int + lpi := len(parts) - 1 + lf := len(frag) + + for i, part := range parts { + if si >= lf { + return parts[i:], true + } + lp := len(part) + // Check for pwc or fwc place holders. + if lp == 1 { + if part[0] == pwc { + index := bytes.IndexByte(frag[si:], tsep) + // We are trying to match pwc and did not find our tsep. + // Will need to move to next node from caller. + if index < 0 { + if i == lpi { + return nil, true + } + return parts[i:], true + } + si += index + 1 + continue + } else if part[0] == fwc { + // If we are here we should be good. + return nil, true + } + } + end := min(si+lp, len(frag)) + // If part is bigger then the fragment, adjust to a portion on the part. + partialPart := lp > end + if partialPart { + // Frag is smaller then part itself. + part = part[:end] + } + if !bytes.Equal(part, frag[si:end]) { + return parts, false + } + // If we still have a portion of the fragment left, update and continue. + if end < lf { + si = end + continue + } + // If we matched a partial, do not move past current part + // but update the part to what was consumed. This allows upper layers to continue. + if end < lp { + if end >= lf { + parts = append([][]byte{}, parts...) // Create a copy before modifying. + parts[i] = parts[i][lf:] + } else { + i++ + } + return parts[i:], true + } + if i == lpi { + return nil, true + } + // If we are here we are not the last part which means we have a wildcard + // gap, so we need to match anything up to next tsep. + si += len(part) + } + return parts, false +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go new file mode 100644 index 000000000..452f5f35e --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go @@ -0,0 +1,380 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +import ( + "bytes" + "sort" +) + +// SubjectTree is an adaptive radix trie (ART) for storing subject information on literal subjects. +// Will use dynamic nodes, path compression and lazy expansion. +// The reason this exists is to not only save some memory in our filestore but to greatly optimize matching +// a wildcard subject to certain members, e.g. consumer NumPending calculations. +type SubjectTree[T any] struct { + root node + size int +} + +// NewSubjectTree creates a new SubjectTree with values T. +func NewSubjectTree[T any]() *SubjectTree[T] { + return &SubjectTree[T]{} +} + +// Size returns the number of elements stored. +func (t *SubjectTree[T]) Size() int { + if t == nil { + return 0 + } + return t.size +} + +// Will empty out the tree, or if tree is nil create a new one. +func (t *SubjectTree[T]) Empty() *SubjectTree[T] { + if t == nil { + return NewSubjectTree[T]() + } + t.root, t.size = nil, 0 + return t +} + +// Insert a value into the tree. Will return if the value was updated and if so the old value. +func (t *SubjectTree[T]) Insert(subject []byte, value T) (*T, bool) { + old, updated := t.insert(&t.root, subject, value, 0) + if !updated { + t.size++ + } + return old, updated +} + +// Find will find the value and return it or false if it was not found. +func (t *SubjectTree[T]) Find(subject []byte) (*T, bool) { + var si uint16 + for n := t.root; n != nil; { + if n.isLeaf() { + if ln := n.(*leaf[T]); ln.match(subject[si:]) { + return &ln.value, true + } + return nil, false + } + // We are a node type here, grab meta portion. + if bn := n.base(); bn.prefixLen > 0 { + end := min(int(si+bn.prefixLen), len(subject)) + if !bytes.Equal(subject[si:end], bn.prefix[:bn.prefixLen]) { + return nil, false + } + // Increment our subject index. + si += bn.prefixLen + } + if an := n.findChild(pivot(subject, si)); an != nil { + n = *an + } else { + return nil, false + } + } + return nil, false +} + +// Delete will delete the item and return its value, or not found if it did not exist. +func (t *SubjectTree[T]) Delete(subject []byte) (*T, bool) { + val, deleted := t.delete(&t.root, subject, 0) + if deleted { + t.size-- + } + return val, deleted +} + +// Match will match against a subject that can have wildcards and invoke the callback func for each matched value. +func (t *SubjectTree[T]) Match(filter []byte, cb func(subject []byte, val *T)) { + if len(filter) == 0 || cb == nil { + return + } + // We need to break this up into chunks based on wildcards, either pwc '*' or fwc '>'. + var raw [16][]byte + parts := genParts(filter, raw[:0]) + var _pre [256]byte + t.match(t.root, parts, _pre[:0], cb) +} + +// Iter will walk all entries in the SubjectTree lexographically. The callback can return false to terminate the walk. +func (t *SubjectTree[T]) Iter(cb func(subject []byte, val *T) bool) { + if t == nil || t.root == nil { + return + } + var _pre [256]byte + t.iter(t.root, _pre[:0], cb) +} + +// Internal methods + +// Internal call to insert that can be recursive. +func (t *SubjectTree[T]) insert(np *node, subject []byte, value T, si int) (*T, bool) { + n := *np + if n == nil { + *np = newLeaf(subject, value) + return nil, false + } + if n.isLeaf() { + ln := n.(*leaf[T]) + if ln.match(subject[si:]) { + // Replace with new value. + old := ln.value + ln.value = value + return &old, true + } + // Here we need to split this leaf. + cpi := commonPrefixLen(ln.suffix, subject[si:]) + nn := newNode4(subject[si : si+cpi]) + ln.setSuffix(ln.suffix[cpi:]) + si += cpi + // Make sure we have different pivot, normally this will be the case unless we have overflowing prefixes. + if p := pivot(ln.suffix, 0); si < len(subject) && p == subject[si] { + // We need to split the original leaf. Recursively call into insert. + t.insert(np, subject, value, si) + // Now add the update version of *np as a child to the new node4. + nn.addChild(p, *np) + } else { + // Can just add this new leaf as a sibling. + nl := newLeaf(subject[si:], value) + nn.addChild(pivot(nl.suffix, 0), nl) + // Add back original. + nn.addChild(pivot(ln.suffix, 0), ln) + } + *np = nn + return nil, false + } + + // Non-leaf nodes. + bn := n.base() + if bn.prefixLen > 0 { + cpi := commonPrefixLen(bn.prefix[:bn.prefixLen], subject[si:]) + if pli := int(bn.prefixLen); cpi >= pli { + // Move past this node. We look for an existing child node to recurse into. + // If one does not exist we can create a new leaf node. + si += pli + if nn := n.findChild(pivot(subject, si)); nn != nil { + return t.insert(nn, subject, value, si) + } + if n.isFull() { + n = n.grow() + *np = n + } + n.addChild(pivot(subject, si), newLeaf(subject[si:], value)) + return nil, false + } else { + // We did not match the prefix completely here. + // Calculate new prefix for this node. + prefix := subject[si : si+cpi] + si += len(prefix) + // We will insert a new node4 and attach our current node below after adjusting prefix. + nn := newNode4(prefix) + // Shift the prefix for our original node. + n.setPrefix(bn.prefix[cpi:bn.prefixLen]) + nn.addChild(pivot(bn.prefix[:], 0), n) + // Add in our new leaf. + nn.addChild(pivot(subject[si:], 0), newLeaf(subject[si:], value)) + // Update our node reference. + *np = nn + } + } else { + if nn := n.findChild(pivot(subject, si)); nn != nil { + return t.insert(nn, subject, value, si) + } + // No prefix and no matched child, so add in new leafnode as needed. + if n.isFull() { + n = n.grow() + *np = n + } + n.addChild(pivot(subject, si), newLeaf(subject[si:], value)) + } + + return nil, false +} + +// internal function to recursively find the leaf to delete. Will do compaction if the item is found and removed. +func (t *SubjectTree[T]) delete(np *node, subject []byte, si uint16) (*T, bool) { + if t == nil || np == nil || *np == nil || len(subject) == 0 { + return nil, false + } + n := *np + if n.isLeaf() { + ln := n.(*leaf[T]) + if ln.match(subject[si:]) { + *np = nil + return &ln.value, true + } + return nil, false + } + // Not a leaf node. + if bn := n.base(); bn.prefixLen > 0 { + if !bytes.Equal(subject[si:si+bn.prefixLen], bn.prefix[:bn.prefixLen]) { + return nil, false + } + // Increment our subject index. + si += bn.prefixLen + } + p := pivot(subject, si) + nna := n.findChild(p) + if nna == nil { + return nil, false + } + nn := *nna + if nn.isLeaf() { + ln := nn.(*leaf[T]) + if ln.match(subject[si:]) { + n.deleteChild(p) + + if sn := n.shrink(); sn != nil { + bn := n.base() + // Make sure to set cap so we force an append to copy below. + pre := bn.prefix[:bn.prefixLen:bn.prefixLen] + // Need to fix up prefixes/suffixes. + if sn.isLeaf() { + ln := sn.(*leaf[T]) + // Make sure to set cap so we force an append to copy. + ln.suffix = append(pre, ln.suffix...) + } else { + // We are a node here, we need to add in the old prefix. + if len(pre) > 0 { + bsn := sn.base() + sn.setPrefix(append(pre, bsn.prefix[:bsn.prefixLen]...)) + } + } + *np = sn + } + + return &ln.value, true + } + return nil, false + } + return t.delete(nna, subject, si) +} + +// Internal function which can be called recursively to match all leaf nodes to a given filter subject which +// once here has been decomposed to parts. These parts only care about wildcards, both pwc and fwc. +func (t *SubjectTree[T]) match(n node, parts [][]byte, pre []byte, cb func(subject []byte, val *T)) { + // Capture if we are sitting on a terminal fwc. + var hasFWC bool + if lp := len(parts); lp > 0 && parts[lp-1][0] == fwc { + hasFWC = true + } + + for n != nil { + nparts, matched := n.matchParts(parts) + // Check if we did not match. + if !matched { + return + } + // We have matched here. If we are a leaf and have exhausted all parts or he have a FWC fire callback. + if n.isLeaf() { + if len(nparts) == 0 || hasFWC { + ln := n.(*leaf[T]) + cb(append(pre, ln.suffix...), &ln.value) + } + return + } + // We have normal nodes here. + // We need to append our prefix + bn := n.base() + if bn.prefixLen > 0 { + // Note that this append may reallocate, but it doesn't modify "pre" at the "match" callsite. + pre = append(pre, bn.prefix[:bn.prefixLen]...) + } + + // Check our remaining parts. + if len(nparts) == 0 && !hasFWC { + // We are a node with no parts left and we are not looking at a fwc. + // We could have a leafnode with no suffix which would be a match. + // We could also have a terminal pwc. Check for those here. + var hasTermPWC bool + if lp := len(parts); lp > 0 && len(parts[lp-1]) == 1 && parts[lp-1][0] == pwc { + // If we are sitting on a terminal pwc, put the pwc back and continue. + nparts = parts[len(parts)-1:] + hasTermPWC = true + } + for _, cn := range n.children() { + if cn == nil { + continue + } + if cn.isLeaf() { + ln := cn.(*leaf[T]) + if len(ln.suffix) == 0 { + cb(append(pre, ln.suffix...), &ln.value) + } else if hasTermPWC && bytes.IndexByte(ln.suffix, tsep) < 0 { + cb(append(pre, ln.suffix...), &ln.value) + } + } else if hasTermPWC { + // We have terminal pwc so call into match again with the child node. + t.match(cn, nparts, pre, cb) + } + } + // Return regardless. + return + } + // If we are sitting on a terminal fwc, put back and continue. + if hasFWC && len(nparts) == 0 { + nparts = parts[len(parts)-1:] + } + + // Here we are a node type with a partial match. + // Check if the first part is a wildcard. + fp := nparts[0] + p := pivot(fp, 0) + // Check if we have a pwc/fwc part here. This will cause us to iterate. + if len(fp) == 1 && (p == pwc || p == fwc) { + // We need to iterate over all children here for the current node + // to see if we match further down. + for _, cn := range n.children() { + if cn != nil { + t.match(cn, nparts, pre, cb) + } + } + } + // Here we have normal traversal, so find the next child. + nn := n.findChild(p) + if nn == nil { + return + } + n, parts = *nn, nparts + } +} + +// Interal iter function to walk nodes in lexigraphical order. +func (t *SubjectTree[T]) iter(n node, pre []byte, cb func(subject []byte, val *T) bool) bool { + if n.isLeaf() { + ln := n.(*leaf[T]) + return cb(append(pre, ln.suffix...), &ln.value) + } + // We are normal node here. + bn := n.base() + // Note that this append may reallocate, but it doesn't modify "pre" at the "iter" callsite. + pre = append(pre, bn.prefix[:bn.prefixLen]...) + // Collect nodes since unsorted. + var _nodes [256]node + nodes := _nodes[:0] + for _, cn := range n.children() { + if cn != nil { + nodes = append(nodes, cn) + } + } + // Now sort. + sort.SliceStable(nodes, func(i, j int) bool { return bytes.Compare(nodes[i].path(), nodes[j].path()) < 0 }) + // Now walk the nodes in order and call into next iter. + for i := range nodes { + if !t.iter(nodes[i], pre, cb) { + return false + } + } + return true +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go new file mode 100644 index 000000000..5ced54bc9 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go @@ -0,0 +1,61 @@ +// Copyright 2023-2024 The NATS Authors +// 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. + +package stree + +// For subject matching. +const ( + pwc = '*' + fwc = '>' + tsep = '.' +) + +// Determine index of common prefix. No match at all is 0, etc. +func commonPrefixLen(s1, s2 []byte) int { + limit := min(len(s1), len(s2)) + var i int + for ; i < limit; i++ { + if s1[i] != s2[i] { + break + } + } + return min(i, maxPrefixLen) +} + +// Helper to copy bytes. +func copyBytes(src []byte) []byte { + if len(src) == 0 { + return nil + } + dst := make([]byte, len(src)) + copy(dst, src) + return dst +} + +type position interface{ int | uint16 } + +// Can return 0 if we have all the subject as prefixes. +func pivot[N position](subject []byte, pos N) byte { + if int(pos) >= len(subject) { + return 0 + } + return subject[pos] +} + +// TODO(dlc) - Can be removed with Go 1.21 once server is on Go 1.22. +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/rs/zerolog/README.md b/vendor/github.com/rs/zerolog/README.md index 972b729fb..afa615693 100644 --- a/vendor/github.com/rs/zerolog/README.md +++ b/vendor/github.com/rs/zerolog/README.md @@ -547,7 +547,7 @@ and facilitates the unification of logging and tracing in some systems: type TracingHook struct{} func (h TracingHook) Run(e *zerolog.Event, level zerolog.Level, msg string) { - ctx := e.Ctx() + ctx := e.GetCtx() spanId := getSpanIdFromContext(ctx) // as per your tracing framework e.Str("span-id", spanId) } diff --git a/vendor/github.com/rs/zerolog/console.go b/vendor/github.com/rs/zerolog/console.go index 282798853..cc6d623e2 100644 --- a/vendor/github.com/rs/zerolog/console.go +++ b/vendor/github.com/rs/zerolog/console.go @@ -76,6 +76,8 @@ type ConsoleWriter struct { FormatErrFieldValue Formatter FormatExtra func(map[string]interface{}, *bytes.Buffer) error + + FormatPrepare func(map[string]interface{}) error } // NewConsoleWriter creates and initializes a new ConsoleWriter. @@ -124,6 +126,13 @@ func (w ConsoleWriter) Write(p []byte) (n int, err error) { return n, fmt.Errorf("cannot decode event: %s", err) } + if w.FormatPrepare != nil { + err = w.FormatPrepare(evt) + if err != nil { + return n, err + } + } + for _, p := range w.PartsOrder { w.writePart(buf, evt, p) } @@ -146,6 +155,15 @@ func (w ConsoleWriter) Write(p []byte) (n int, err error) { return len(p), err } +// Call the underlying writer's Close method if it is an io.Closer. Otherwise +// does nothing. +func (w ConsoleWriter) Close() error { + if closer, ok := w.Out.(io.Closer); ok { + return closer.Close() + } + return nil +} + // writeFields appends formatted key-value pairs to buf. func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer) { var fields = make([]string, 0, len(evt)) @@ -272,7 +290,7 @@ func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, } case MessageFieldName: if w.FormatMessage == nil { - f = consoleDefaultFormatMessage + f = consoleDefaultFormatMessage(w.NoColor, evt[LevelFieldName]) } else { f = w.FormatMessage } @@ -310,10 +328,10 @@ func needsQuote(s string) bool { return false } -// colorize returns the string s wrapped in ANSI code c, unless disabled is true. +// colorize returns the string s wrapped in ANSI code c, unless disabled is true or c is 0. func colorize(s interface{}, c int, disabled bool) string { e := os.Getenv("NO_COLOR") - if e != "" { + if e != "" || c == 0 { disabled = true } @@ -378,27 +396,16 @@ func consoleDefaultFormatLevel(noColor bool) Formatter { return func(i interface{}) string { var l string if ll, ok := i.(string); ok { - switch ll { - case LevelTraceValue: - l = colorize("TRC", colorMagenta, noColor) - case LevelDebugValue: - l = colorize("DBG", colorYellow, noColor) - case LevelInfoValue: - l = colorize("INF", colorGreen, noColor) - case LevelWarnValue: - l = colorize("WRN", colorRed, noColor) - case LevelErrorValue: - l = colorize(colorize("ERR", colorRed, noColor), colorBold, noColor) - case LevelFatalValue: - l = colorize(colorize("FTL", colorRed, noColor), colorBold, noColor) - case LevelPanicValue: - l = colorize(colorize("PNC", colorRed, noColor), colorBold, noColor) - default: - l = colorize(ll, colorBold, noColor) + level, _ := ParseLevel(ll) + fl, ok := FormattedLevels[level] + if ok { + l = colorize(fl, LevelColors[level], noColor) + } else { + l = strings.ToUpper(ll)[0:3] } } else { if i == nil { - l = colorize("???", colorBold, noColor) + l = "???" } else { l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3] } @@ -425,11 +432,18 @@ func consoleDefaultFormatCaller(noColor bool) Formatter { } } -func consoleDefaultFormatMessage(i interface{}) string { - if i == nil { - return "" +func consoleDefaultFormatMessage(noColor bool, level interface{}) Formatter { + return func(i interface{}) string { + if i == nil || i == "" { + return "" + } + switch level { + case LevelInfoValue, LevelWarnValue, LevelErrorValue, LevelFatalValue, LevelPanicValue: + return colorize(fmt.Sprintf("%s", i), colorBold, noColor) + default: + return fmt.Sprintf("%s", i) + } } - return fmt.Sprintf("%s", i) } func consoleDefaultFormatFieldName(noColor bool) Formatter { @@ -450,6 +464,6 @@ func consoleDefaultFormatErrFieldName(noColor bool) Formatter { func consoleDefaultFormatErrFieldValue(noColor bool) Formatter { return func(i interface{}) string { - return colorize(fmt.Sprintf("%s", i), colorRed, noColor) + return colorize(colorize(fmt.Sprintf("%s", i), colorBold, noColor), colorRed, noColor) } } diff --git a/vendor/github.com/rs/zerolog/context.go b/vendor/github.com/rs/zerolog/context.go index fc62ad9c1..df352eb66 100644 --- a/vendor/github.com/rs/zerolog/context.go +++ b/vendor/github.com/rs/zerolog/context.go @@ -3,7 +3,7 @@ package zerolog import ( "context" "fmt" - "io/ioutil" + "io" "math" "net" "time" @@ -23,7 +23,7 @@ func (c Context) Logger() Logger { // Only map[string]interface{} and []interface{} are accepted. []interface{} must // alternate string keys and arbitrary values, and extraneous ones are ignored. func (c Context) Fields(fields interface{}) Context { - c.l.context = appendFields(c.l.context, fields) + c.l.context = appendFields(c.l.context, fields, c.l.stack) return c } @@ -57,7 +57,7 @@ func (c Context) Array(key string, arr LogArrayMarshaler) Context { // Object marshals an object that implement the LogObjectMarshaler interface. func (c Context) Object(key string, obj LogObjectMarshaler) Context { - e := newEvent(LevelWriterAdapter{ioutil.Discard}, 0) + e := newEvent(LevelWriterAdapter{io.Discard}, 0) e.Object(key, obj) c.l.context = enc.AppendObjectData(c.l.context, e.buf) putEvent(e) @@ -66,7 +66,7 @@ func (c Context) Object(key string, obj LogObjectMarshaler) Context { // EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface. func (c Context) EmbedObject(obj LogObjectMarshaler) Context { - e := newEvent(LevelWriterAdapter{ioutil.Discard}, 0) + e := newEvent(LevelWriterAdapter{io.Discard}, 0) e.EmbedObject(obj) c.l.context = enc.AppendObjectData(c.l.context, e.buf) putEvent(e) @@ -163,6 +163,22 @@ func (c Context) Errs(key string, errs []error) Context { // Err adds the field "error" with serialized err to the logger context. func (c Context) Err(err error) Context { + if c.l.stack && ErrorStackMarshaler != nil { + switch m := ErrorStackMarshaler(err).(type) { + case nil: + case LogObjectMarshaler: + c = c.Object(ErrorStackFieldName, m) + case error: + if m != nil && !isNilValue(m) { + c = c.Str(ErrorStackFieldName, m.Error()) + } + case string: + c = c.Str(ErrorStackFieldName, m) + default: + c = c.Interface(ErrorStackFieldName, m) + } + } + return c.AnErr(ErrorFieldName, err) } @@ -375,10 +391,19 @@ func (c Context) Durs(key string, d []time.Duration) Context { // Interface adds the field key with obj marshaled using reflection. func (c Context) Interface(key string, i interface{}) Context { + if obj, ok := i.(LogObjectMarshaler); ok { + return c.Object(key, obj) + } c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), i) return c } +// Type adds the field key with val's type using reflection. +func (c Context) Type(key string, val interface{}) Context { + c.l.context = enc.AppendType(enc.AppendKey(c.l.context, key), val) + return c +} + // Any is a wrapper around Context.Interface. func (c Context) Any(key string, i interface{}) Context { return c.Interface(key, i) diff --git a/vendor/github.com/rs/zerolog/event.go b/vendor/github.com/rs/zerolog/event.go index 2a5d3b087..5c949f8a7 100644 --- a/vendor/github.com/rs/zerolog/event.go +++ b/vendor/github.com/rs/zerolog/event.go @@ -164,7 +164,7 @@ func (e *Event) Fields(fields interface{}) *Event { if e == nil { return e } - e.buf = appendFields(e.buf, fields) + e.buf = appendFields(e.buf, fields, e.stack) return e } diff --git a/vendor/github.com/rs/zerolog/example.jsonl b/vendor/github.com/rs/zerolog/example.jsonl new file mode 100644 index 000000000..d73193d7d --- /dev/null +++ b/vendor/github.com/rs/zerolog/example.jsonl @@ -0,0 +1,7 @@ +{"time":"5:41PM","level":"info","message":"Starting listener","listen":":8080","pid":37556} +{"time":"5:41PM","level":"debug","message":"Access","database":"myapp","host":"localhost:4962","pid":37556} +{"time":"5:41PM","level":"info","message":"Access","method":"GET","path":"/users","pid":37556,"resp_time":23} +{"time":"5:41PM","level":"info","message":"Access","method":"POST","path":"/posts","pid":37556,"resp_time":532} +{"time":"5:41PM","level":"warn","message":"Slow request","method":"POST","path":"/posts","pid":37556,"resp_time":532} +{"time":"5:41PM","level":"info","message":"Access","method":"GET","path":"/users","pid":37556,"resp_time":10} +{"time":"5:41PM","level":"error","message":"Database connection lost","database":"myapp","pid":37556,"error":"connection reset by peer"} diff --git a/vendor/github.com/rs/zerolog/fields.go b/vendor/github.com/rs/zerolog/fields.go index c1eb5ce79..23606ddd5 100644 --- a/vendor/github.com/rs/zerolog/fields.go +++ b/vendor/github.com/rs/zerolog/fields.go @@ -12,13 +12,13 @@ func isNilValue(i interface{}) bool { return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0 } -func appendFields(dst []byte, fields interface{}) []byte { +func appendFields(dst []byte, fields interface{}, stack bool) []byte { switch fields := fields.(type) { case []interface{}: if n := len(fields); n&0x1 == 1 { // odd number fields = fields[:n-1] } - dst = appendFieldList(dst, fields) + dst = appendFieldList(dst, fields, stack) case map[string]interface{}: keys := make([]string, 0, len(fields)) for key := range fields { @@ -28,13 +28,13 @@ func appendFields(dst []byte, fields interface{}) []byte { kv := make([]interface{}, 2) for _, key := range keys { kv[0], kv[1] = key, fields[key] - dst = appendFieldList(dst, kv) + dst = appendFieldList(dst, kv, stack) } } return dst } -func appendFieldList(dst []byte, kvList []interface{}) []byte { +func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte { for i, n := 0, len(kvList); i < n; i += 2 { key, val := kvList[i], kvList[i+1] if key, ok := key.(string); ok { @@ -74,6 +74,21 @@ func appendFieldList(dst []byte, kvList []interface{}) []byte { default: dst = enc.AppendInterface(dst, m) } + + if stack && ErrorStackMarshaler != nil { + dst = enc.AppendKey(dst, ErrorStackFieldName) + switch m := ErrorStackMarshaler(val).(type) { + case nil: + case error: + if m != nil && !isNilValue(m) { + dst = enc.AppendString(dst, m.Error()) + } + case string: + dst = enc.AppendString(dst, m) + default: + dst = enc.AppendInterface(dst, m) + } + } case []error: dst = enc.AppendArrayStart(dst) for i, err := range val { diff --git a/vendor/github.com/rs/zerolog/globals.go b/vendor/github.com/rs/zerolog/globals.go index e1067deb6..b38a7fcee 100644 --- a/vendor/github.com/rs/zerolog/globals.go +++ b/vendor/github.com/rs/zerolog/globals.go @@ -108,6 +108,34 @@ var ( // DefaultContextLogger is returned from Ctx() if there is no logger associated // with the context. DefaultContextLogger *Logger + + // LevelColors are used by ConsoleWriter's consoleDefaultFormatLevel to color + // log levels. + LevelColors = map[Level]int{ + TraceLevel: colorBlue, + DebugLevel: 0, + InfoLevel: colorGreen, + WarnLevel: colorYellow, + ErrorLevel: colorRed, + FatalLevel: colorRed, + PanicLevel: colorRed, + } + + // FormattedLevels are used by ConsoleWriter's consoleDefaultFormatLevel + // for a short level name. + FormattedLevels = map[Level]string{ + TraceLevel: "TRC", + DebugLevel: "DBG", + InfoLevel: "INF", + WarnLevel: "WRN", + ErrorLevel: "ERR", + FatalLevel: "FTL", + PanicLevel: "PNC", + } + + // TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped + // from the TriggerLevelWriter buffer pool if the buffer grows above the limit. + TriggerLevelWriterBufferReuseLimit = 64 * 1024 ) var ( diff --git a/vendor/github.com/rs/zerolog/log.go b/vendor/github.com/rs/zerolog/log.go index 834c7e604..9fec7cc31 100644 --- a/vendor/github.com/rs/zerolog/log.go +++ b/vendor/github.com/rs/zerolog/log.go @@ -118,7 +118,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "strconv" "strings" @@ -246,7 +245,7 @@ type Logger struct { // you may consider using sync wrapper. func New(w io.Writer) Logger { if w == nil { - w = ioutil.Discard + w = io.Discard } lw, ok := w.(LevelWriter) if !ok { @@ -326,10 +325,13 @@ func (l Logger) Sample(s Sampler) Logger { } // Hook returns a logger with the h Hook. -func (l Logger) Hook(h Hook) Logger { - newHooks := make([]Hook, len(l.hooks), len(l.hooks)+1) +func (l Logger) Hook(hooks ...Hook) Logger { + if len(hooks) == 0 { + return l + } + newHooks := make([]Hook, len(l.hooks), len(l.hooks)+len(hooks)) copy(newHooks, l.hooks) - l.hooks = append(newHooks, h) + l.hooks = append(newHooks, hooks...) return l } @@ -385,7 +387,14 @@ func (l *Logger) Err(err error) *Event { // // You must call Msg on the returned event in order to send the event. func (l *Logger) Fatal() *Event { - return l.newEvent(FatalLevel, func(msg string) { os.Exit(1) }) + return l.newEvent(FatalLevel, func(msg string) { + if closer, ok := l.w.(io.Closer); ok { + // Close the writer to flush any buffered message. Otherwise the message + // will be lost as os.Exit() terminates the program immediately. + closer.Close() + } + os.Exit(1) + }) } // Panic starts a new message with panic level. The panic() function @@ -450,6 +459,14 @@ func (l *Logger) Printf(format string, v ...interface{}) { } } +// Println sends a log event using debug level and no extra field. +// Arguments are handled in the manner of fmt.Println. +func (l *Logger) Println(v ...interface{}) { + if e := l.Debug(); e.Enabled() { + e.CallerSkipFrame(1).Msg(fmt.Sprintln(v...)) + } +} + // Write implements the io.Writer interface. This is useful to set as a writer // for the standard library log. func (l Logger) Write(p []byte) (n int, err error) { @@ -488,6 +505,9 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event { // should returns true if the log event should be logged. func (l *Logger) should(lvl Level) bool { + if l.w == nil { + return false + } if lvl < l.level || lvl < GlobalLevel() { return false } diff --git a/vendor/github.com/rs/zerolog/pretty.png b/vendor/github.com/rs/zerolog/pretty.png index 242033686..1449e45d1 100644 Binary files a/vendor/github.com/rs/zerolog/pretty.png and b/vendor/github.com/rs/zerolog/pretty.png differ diff --git a/vendor/github.com/rs/zerolog/syslog.go b/vendor/github.com/rs/zerolog/syslog.go index c40828307..a2b7285d8 100644 --- a/vendor/github.com/rs/zerolog/syslog.go +++ b/vendor/github.com/rs/zerolog/syslog.go @@ -78,3 +78,12 @@ func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) { n = len(p) return } + +// Call the underlying writer's Close method if it is an io.Closer. Otherwise +// does nothing. +func (sw syslogWriter) Close() error { + if c, ok := sw.w.(io.Closer); ok { + return c.Close() + } + return nil +} diff --git a/vendor/github.com/rs/zerolog/writer.go b/vendor/github.com/rs/zerolog/writer.go index 9b9ef88e8..41b394d71 100644 --- a/vendor/github.com/rs/zerolog/writer.go +++ b/vendor/github.com/rs/zerolog/writer.go @@ -27,6 +27,15 @@ func (lw LevelWriterAdapter) WriteLevel(l Level, p []byte) (n int, err error) { return lw.Write(p) } +// Call the underlying writer's Close method if it is an io.Closer. Otherwise +// does nothing. +func (lw LevelWriterAdapter) Close() error { + if closer, ok := lw.Writer.(io.Closer); ok { + return closer.Close() + } + return nil +} + type syncWriter struct { mu sync.Mutex lw LevelWriter @@ -57,6 +66,15 @@ func (s *syncWriter) WriteLevel(l Level, p []byte) (n int, err error) { return s.lw.WriteLevel(l, p) } +func (s *syncWriter) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + if closer, ok := s.lw.(io.Closer); ok { + return closer.Close() + } + return nil +} + type multiLevelWriter struct { writers []LevelWriter } @@ -89,6 +107,20 @@ func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) { return n, err } +// Calls close on all the underlying writers that are io.Closers. If any of the +// Close methods return an error, the remainder of the closers are not closed +// and the error is returned. +func (t multiLevelWriter) Close() error { + for _, w := range t.writers { + if closer, ok := w.(io.Closer); ok { + if err := closer.Close(); err != nil { + return err + } + } + } + return nil +} + // MultiLevelWriter creates a writer that duplicates its writes to all the // provided writers, similar to the Unix tee(1) command. If some writers // implement LevelWriter, their WriteLevel method will be used instead of Write. @@ -180,3 +212,135 @@ func (w *FilteredLevelWriter) WriteLevel(level Level, p []byte) (int, error) { } return len(p), nil } + +var triggerWriterPool = &sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(make([]byte, 0, 1024)) + }, +} + +// TriggerLevelWriter buffers log lines at the ConditionalLevel or below +// until a trigger level (or higher) line is emitted. Log lines with level +// higher than ConditionalLevel are always written out to the destination +// writer. If trigger never happens, buffered log lines are never written out. +// +// It can be used to configure "log level per request". +type TriggerLevelWriter struct { + // Destination writer. If LevelWriter is provided (usually), its WriteLevel is used + // instead of Write. + io.Writer + + // ConditionalLevel is the level (and below) at which lines are buffered until + // a trigger level (or higher) line is emitted. Usually this is set to DebugLevel. + ConditionalLevel Level + + // TriggerLevel is the lowest level that triggers the sending of the conditional + // level lines. Usually this is set to ErrorLevel. + TriggerLevel Level + + buf *bytes.Buffer + triggered bool + mu sync.Mutex +} + +func (w *TriggerLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + + // At first trigger level or above log line, we flush the buffer and change the + // trigger state to triggered. + if !w.triggered && l >= w.TriggerLevel { + err := w.trigger() + if err != nil { + return 0, err + } + } + + // Unless triggered, we buffer everything at and below ConditionalLevel. + if !w.triggered && l <= w.ConditionalLevel { + if w.buf == nil { + w.buf = triggerWriterPool.Get().(*bytes.Buffer) + } + + // We prefix each log line with a byte with the level. + // Hopefully we will never have a level value which equals a newline + // (which could interfere with reconstruction of log lines in the trigger method). + w.buf.WriteByte(byte(l)) + w.buf.Write(p) + return len(p), nil + } + + // Anything above ConditionalLevel is always passed through. + // Once triggered, everything is passed through. + if lw, ok := w.Writer.(LevelWriter); ok { + return lw.WriteLevel(l, p) + } + return w.Write(p) +} + +// trigger expects lock to be held. +func (w *TriggerLevelWriter) trigger() error { + if w.triggered { + return nil + } + w.triggered = true + + if w.buf == nil { + return nil + } + + p := w.buf.Bytes() + for len(p) > 0 { + // We do not use bufio.Scanner here because we already have full buffer + // in the memory and we do not want extra copying from the buffer to + // scanner's token slice, nor we want to hit scanner's token size limit, + // and we also want to preserve newlines. + i := bytes.IndexByte(p, '\n') + line := p[0 : i+1] + p = p[i+1:] + // We prefixed each log line with a byte with the level. + level := Level(line[0]) + line = line[1:] + var err error + if lw, ok := w.Writer.(LevelWriter); ok { + _, err = lw.WriteLevel(level, line) + } else { + _, err = w.Write(line) + } + if err != nil { + return err + } + } + + return nil +} + +// Trigger forces flushing the buffer and change the trigger state to +// triggered, if the writer has not already been triggered before. +func (w *TriggerLevelWriter) Trigger() error { + w.mu.Lock() + defer w.mu.Unlock() + + return w.trigger() +} + +// Close closes the writer and returns the buffer to the pool. +func (w *TriggerLevelWriter) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + + if w.buf == nil { + return nil + } + + // We return the buffer only if it has not grown above the limit. + // This prevents accumulation of large buffers in the pool just + // because occasionally a large buffer might be needed. + if w.buf.Cap() <= TriggerLevelWriterBufferReuseLimit { + w.buf.Reset() + triggerWriterPool.Put(w.buf) + } + w.buf = nil + + return nil +} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go index 9509014e8..c6f438774 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go @@ -31,10 +31,24 @@ const ( // Server HTTP metrics. const ( - RequestCount = "http.server.request_count" // Incoming request count total - RequestContentLength = "http.server.request_content_length" // Incoming request bytes total + // Deprecated: This field is unused. + RequestCount = "http.server.request_count" // Incoming request count total + // Deprecated: Use of this field has been migrated to serverRequestSize. It will be removed in a future version. + RequestContentLength = "http.server.request_content_length" // Incoming request bytes total + // Deprecated: Use of this field has been migrated to serverResponseSize. It will be removed in a future version. ResponseContentLength = "http.server.response_content_length" // Incoming response bytes total - ServerLatency = "http.server.duration" // Incoming end to end duration, milliseconds + // Deprecated: Use of this field has been migrated to serverDuration. It will be removed in a future version. + ServerLatency = "http.server.duration" // Incoming end to end duration, milliseconds + serverRequestSize = "http.server.request.size" // Incoming request bytes total + serverResponseSize = "http.server.response.size" // Incoming response bytes total + serverDuration = "http.server.duration" // Incoming end to end duration, milliseconds +) + +// Client HTTP metrics. +const ( + clientRequestSize = "http.client.request.size" // Outgoing request bytes total + clientResponseSize = "http.client.response.size" // Outgoing response bytes total + clientDuration = "http.client.duration" // Outgoing end to end duration, milliseconds ) // Filter is a predicate used to determine whether a given http.request should diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go index af84f0e4b..3d292dab6 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go @@ -108,23 +108,23 @@ func handleErr(err error) { func (h *middleware) createMeasures() { var err error h.requestBytesCounter, err = h.meter.Int64Counter( - RequestContentLength, + serverRequestSize, metric.WithUnit("By"), - metric.WithDescription("Measures the size of HTTP request content length (uncompressed)"), + metric.WithDescription("Measures the size of HTTP request messages."), ) handleErr(err) h.responseBytesCounter, err = h.meter.Int64Counter( - ResponseContentLength, + serverResponseSize, metric.WithUnit("By"), - metric.WithDescription("Measures the size of HTTP response content length (uncompressed)"), + metric.WithDescription("Measures the size of HTTP response messages."), ) handleErr(err) h.serverLatencyMeasure, err = h.meter.Float64Histogram( - ServerLatency, + serverDuration, metric.WithUnit("ms"), - metric.WithDescription("Measures the duration of HTTP request handling"), + metric.WithDescription("Measures the duration of inbound HTTP requests."), ) handleErr(err) } diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go index 794d4c26a..495d700cf 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go @@ -51,6 +51,14 @@ func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } +// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client. +// The following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the +// related values are defined in req: "net.peer.port". +func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue { + return hc.ClientRequestMetrics(req) +} + // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { @@ -114,36 +122,6 @@ func HTTPServerStatus(code int) (codes.Code, string) { return hc.ServerStatus(code) } -// HTTPRequestHeader returns the contents of h as attributes. -// -// Instrumentation should require an explicit configuration of which headers to -// captured and then prune what they pass here. Including all headers can be a -// security risk - explicit configuration helps avoid leaking sensitive -// information. -// -// The User-Agent header is already captured in the http.user_agent attribute -// from ClientRequest and ServerRequest. Instrumentation may provide an option -// to capture that header here even though it is not recommended. Otherwise, -// instrumentation should filter that out of what is passed. -func HTTPRequestHeader(h http.Header) []attribute.KeyValue { - return hc.RequestHeader(h) -} - -// HTTPResponseHeader returns the contents of h as attributes. -// -// Instrumentation should require an explicit configuration of which headers to -// captured and then prune what they pass here. Including all headers can be a -// security risk - explicit configuration helps avoid leaking sensitive -// information. -// -// The User-Agent header is already captured in the http.user_agent attribute -// from ClientRequest and ServerRequest. Instrumentation may provide an option -// to capture that header here even though it is not recommended. Otherwise, -// instrumentation should filter that out of what is passed. -func HTTPResponseHeader(h http.Header) []attribute.KeyValue { - return hc.ResponseHeader(h) -} - // httpConv are the HTTP semantic convention attributes defined for a version // of the OpenTelemetry specification. type httpConv struct { @@ -286,6 +264,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { return attrs } +// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The +// following attributes are always returned: "http.method", "net.peer.name". +// The following attributes are returned if the related values +// are defined in req: "net.peer.port". +func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue { + /* The following semantic conventions are returned if present: + http.method string + net.peer.name string + net.peer.port int + */ + + n := 2 // method, peer name. + var h string + if req.URL != nil { + h = req.URL.Host + } + peer, p := firstHostPort(h, req.Header.Get("Host")) + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) + if port > 0 { + n++ + } + + attrs := make([]attribute.KeyValue, 0, n) + attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer)) + + if port > 0 { + attrs = append(attrs, c.NetConv.PeerPort(port)) + } + + return attrs +} + // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this @@ -551,31 +561,6 @@ func firstHostPort(source ...string) (host string, port int) { return } -// RequestHeader returns the contents of h as OpenTelemetry attributes. -func (c *httpConv) RequestHeader(h http.Header) []attribute.KeyValue { - return c.header("http.request.header", h) -} - -// ResponseHeader returns the contents of h as OpenTelemetry attributes. -func (c *httpConv) ResponseHeader(h http.Header) []attribute.KeyValue { - return c.header("http.response.header", h) -} - -func (c *httpConv) header(prefix string, h http.Header) []attribute.KeyValue { - key := func(k string) attribute.Key { - k = strings.ToLower(k) - k = strings.ReplaceAll(k, "-", "_") - k = fmt.Sprintf("%s.%s", prefix, k) - return attribute.Key(k) - } - - attrs := make([]attribute.KeyValue, 0, len(h)) - for k, v := range h { - attrs = append(attrs, key(k).StringSlice(v)) - } - return attrs -} - // ClientStatus returns a span status code and message for an HTTP status code // value received by a client. func (c *httpConv) ClientStatus(code int) (codes.Code, string) { diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go index cb4cb9355..d3a06e0ca 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go @@ -32,24 +32,6 @@ func NetTransport(network string) attribute.KeyValue { return nc.Transport(network) } -// NetClient returns trace attributes for a client network connection to address. -// See net.Dial for information about acceptable address values, address should -// be the same as the one used to create conn. If conn is nil, only network -// peer attributes will be returned that describe address. Otherwise, the -// socket level information about conn will also be included. -func NetClient(address string, conn net.Conn) []attribute.KeyValue { - return nc.Client(address, conn) -} - -// NetServer returns trace attributes for a network listener listening at address. -// See net.Listen for information about acceptable address values, address -// should be the same as the one used to create ln. If ln is nil, only network -// host attributes will be returned that describe address. Otherwise, the -// socket level information about ln will also be included. -func NetServer(address string, ln net.Listener) []attribute.KeyValue { - return nc.Server(address, ln) -} - // netConv are the network semantic convention attributes defined for a version // of the OpenTelemetry specification. type netConv struct { @@ -125,52 +107,6 @@ func (c *netConv) Host(address string) []attribute.KeyValue { return attrs } -// Server returns attributes for a network listener listening at address. See -// net.Listen for information about acceptable address values, address should -// be the same as the one used to create ln. If ln is nil, only network host -// attributes will be returned that describe address. Otherwise, the socket -// level information about ln will also be included. -func (c *netConv) Server(address string, ln net.Listener) []attribute.KeyValue { - if ln == nil { - return c.Host(address) - } - - lAddr := ln.Addr() - if lAddr == nil { - return c.Host(address) - } - - hostName, hostPort := splitHostPort(address) - sockHostAddr, sockHostPort := splitHostPort(lAddr.String()) - network := lAddr.Network() - sockFamily := family(network, sockHostAddr) - - n := nonZeroStr(hostName, network, sockHostAddr, sockFamily) - n += positiveInt(hostPort, sockHostPort) - attr := make([]attribute.KeyValue, 0, n) - if hostName != "" { - attr = append(attr, c.HostName(hostName)) - if hostPort > 0 { - // Only if net.host.name is set should net.host.port be. - attr = append(attr, c.HostPort(hostPort)) - } - } - if network != "" { - attr = append(attr, c.Transport(network)) - } - if sockFamily != "" { - attr = append(attr, c.NetSockFamilyKey.String(sockFamily)) - } - if sockHostAddr != "" { - attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr)) - if sockHostPort > 0 { - // Only if net.sock.host.addr is set should net.sock.host.port be. - attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort)) - } - } - return attr -} - func (c *netConv) HostName(name string) attribute.KeyValue { return c.NetHostNameKey.String(name) } @@ -179,85 +115,6 @@ func (c *netConv) HostPort(port int) attribute.KeyValue { return c.NetHostPortKey.Int(port) } -// Client returns attributes for a client network connection to address. See -// net.Dial for information about acceptable address values, address should be -// the same as the one used to create conn. If conn is nil, only network peer -// attributes will be returned that describe address. Otherwise, the socket -// level information about conn will also be included. -func (c *netConv) Client(address string, conn net.Conn) []attribute.KeyValue { - if conn == nil { - return c.Peer(address) - } - - lAddr, rAddr := conn.LocalAddr(), conn.RemoteAddr() - - var network string - switch { - case lAddr != nil: - network = lAddr.Network() - case rAddr != nil: - network = rAddr.Network() - default: - return c.Peer(address) - } - - peerName, peerPort := splitHostPort(address) - var ( - sockFamily string - sockPeerAddr string - sockPeerPort int - sockHostAddr string - sockHostPort int - ) - - if lAddr != nil { - sockHostAddr, sockHostPort = splitHostPort(lAddr.String()) - } - - if rAddr != nil { - sockPeerAddr, sockPeerPort = splitHostPort(rAddr.String()) - } - - switch { - case sockHostAddr != "": - sockFamily = family(network, sockHostAddr) - case sockPeerAddr != "": - sockFamily = family(network, sockPeerAddr) - } - - n := nonZeroStr(peerName, network, sockPeerAddr, sockHostAddr, sockFamily) - n += positiveInt(peerPort, sockPeerPort, sockHostPort) - attr := make([]attribute.KeyValue, 0, n) - if peerName != "" { - attr = append(attr, c.PeerName(peerName)) - if peerPort > 0 { - // Only if net.peer.name is set should net.peer.port be. - attr = append(attr, c.PeerPort(peerPort)) - } - } - if network != "" { - attr = append(attr, c.Transport(network)) - } - if sockFamily != "" { - attr = append(attr, c.NetSockFamilyKey.String(sockFamily)) - } - if sockPeerAddr != "" { - attr = append(attr, c.NetSockPeerAddrKey.String(sockPeerAddr)) - if sockPeerPort > 0 { - // Only if net.sock.peer.addr is set should net.sock.peer.port be. - attr = append(attr, c.NetSockPeerPortKey.Int(sockPeerPort)) - } - } - if sockHostAddr != "" { - attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr)) - if sockHostPort > 0 { - // Only if net.sock.host.addr is set should net.sock.host.port be. - attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort)) - } - } - return attr -} - func family(network, address string) string { switch network { case "unix", "unixgram", "unixpacket": @@ -273,26 +130,6 @@ func family(network, address string) string { return "" } -func nonZeroStr(strs ...string) int { - var n int - for _, str := range strs { - if str != "" { - n++ - } - } - return n -} - -func positiveInt(ints ...int) int { - var n int - for _, i := range ints { - if i > 0 { - n++ - } - } - return n -} - // Peer returns attributes for a network peer address. func (c *netConv) Peer(address string) []attribute.KeyValue { h, p := splitHostPort(address) diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go index e835cac12..8d850df3b 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go @@ -19,31 +19,43 @@ import ( "io" "net/http" "net/http/httptrace" + "sync/atomic" + "time" + + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" + + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" ) // Transport implements the http.RoundTripper interface and wraps -// outbound HTTP(S) requests with a span. +// outbound HTTP(S) requests with a span and enriches it with metrics. type Transport struct { rt http.RoundTripper tracer trace.Tracer + meter metric.Meter propagators propagation.TextMapPropagator spanStartOptions []trace.SpanStartOption filters []Filter spanNameFormatter func(string, *http.Request) string clientTrace func(context.Context) *httptrace.ClientTrace + + requestBytesCounter metric.Int64Counter + responseBytesCounter metric.Int64Counter + latencyMeasure metric.Float64Histogram } var _ http.RoundTripper = &Transport{} // NewTransport wraps the provided http.RoundTripper with one that -// starts a span and injects the span context into the outbound request headers. +// starts a span, injects the span context into the outbound request headers, +// and enriches it with metrics. // // If the provided http.RoundTripper is nil, http.DefaultTransport will be used // as the base http.RoundTripper. @@ -63,12 +75,14 @@ func NewTransport(base http.RoundTripper, opts ...Option) *Transport { c := newConfig(append(defaultOpts, opts...)...) t.applyConfig(c) + t.createMeasures() return &t } func (t *Transport) applyConfig(c *config) { t.tracer = c.Tracer + t.meter = c.Meter t.propagators = c.Propagators t.spanStartOptions = c.SpanStartOptions t.filters = c.Filters @@ -76,6 +90,30 @@ func (t *Transport) applyConfig(c *config) { t.clientTrace = c.ClientTrace } +func (t *Transport) createMeasures() { + var err error + t.requestBytesCounter, err = t.meter.Int64Counter( + clientRequestSize, + metric.WithUnit("By"), + metric.WithDescription("Measures the size of HTTP request messages."), + ) + handleErr(err) + + t.responseBytesCounter, err = t.meter.Int64Counter( + clientResponseSize, + metric.WithUnit("By"), + metric.WithDescription("Measures the size of HTTP response messages."), + ) + handleErr(err) + + t.latencyMeasure, err = t.meter.Float64Histogram( + clientDuration, + metric.WithUnit("ms"), + metric.WithDescription("Measures the duration of outbound HTTP requests."), + ) + handleErr(err) +} + func defaultTransportFormatter(_ string, r *http.Request) string { return "HTTP " + r.Method } @@ -84,6 +122,7 @@ func defaultTransportFormatter(_ string, r *http.Request) string { // before handing the request to the configured base RoundTripper. The created span will // end when the response body is closed or when a read from the body returns io.EOF. func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { + requestStartTime := time.Now() for _, f := range t.filters { if !f(r) { // Simply pass through to the base RoundTripper if a filter rejects the request @@ -109,7 +148,23 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx)) } + labeler := &Labeler{} + ctx = injectLabeler(ctx, labeler) + r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request. + + // use a body wrapper to determine the request size + var bw bodyWrapper + // if request body is nil or NoBody, we don't want to mutate the body as it + // will affect the identity of it in an unforeseeable way because we assert + // ReadCloser fulfills a certain interface and it is indeed nil or NoBody. + if r.Body != nil && r.Body != http.NoBody { + bw.ReadCloser = r.Body + // noop to prevent nil panic. not using this record fun yet. + bw.record = func(int64) {} + r.Body = &bw + } + span.SetAttributes(semconvutil.HTTPClientRequest(r)...) t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header)) @@ -121,9 +176,28 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { return res, err } + // metrics + metricAttrs := append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...) + if res.StatusCode > 0 { + metricAttrs = append(metricAttrs, semconv.HTTPStatusCode(res.StatusCode)) + } + o := metric.WithAttributes(metricAttrs...) + t.requestBytesCounter.Add(ctx, bw.read, o) + // For handling response bytes we leverage a callback when the client reads the http response + readRecordFunc := func(n int64) { + t.responseBytesCounter.Add(ctx, n, o) + } + + // traces span.SetAttributes(semconvutil.HTTPClientResponse(res)...) span.SetStatus(semconvutil.HTTPClientStatus(res.StatusCode)) - res.Body = newWrappedBody(span, res.Body) + + res.Body = newWrappedBody(span, readRecordFunc, res.Body) + + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond) + + t.latencyMeasure.Record(ctx, elapsedTime, o) return res, err } @@ -131,17 +205,17 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { // newWrappedBody returns a new and appropriately scoped *wrappedBody as an // io.ReadCloser. If the passed body implements io.Writer, the returned value // will implement io.ReadWriteCloser. -func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser { +func newWrappedBody(span trace.Span, record func(n int64), body io.ReadCloser) io.ReadCloser { // The successful protocol switch responses will have a body that // implement an io.ReadWriteCloser. Ensure this interface type continues // to be satisfied if that is the case. if _, ok := body.(io.ReadWriteCloser); ok { - return &wrappedBody{span: span, body: body} + return &wrappedBody{span: span, record: record, body: body} } // Remove the implementation of the io.ReadWriteCloser and only implement // the io.ReadCloser. - return struct{ io.ReadCloser }{&wrappedBody{span: span, body: body}} + return struct{ io.ReadCloser }{&wrappedBody{span: span, record: record, body: body}} } // wrappedBody is the response body type returned by the transport @@ -153,8 +227,11 @@ func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser { // If the response body implements the io.Writer interface (i.e. for // successful protocol switches), the wrapped body also will. type wrappedBody struct { - span trace.Span - body io.ReadCloser + span trace.Span + recorded atomic.Bool + record func(n int64) + body io.ReadCloser + read atomic.Int64 } var _ io.ReadWriteCloser = &wrappedBody{} @@ -171,11 +248,14 @@ func (wb *wrappedBody) Write(p []byte) (int, error) { func (wb *wrappedBody) Read(b []byte) (int, error) { n, err := wb.body.Read(b) + // Record the number of bytes read + wb.read.Add(int64(n)) switch err { case nil: // nothing to do here but fall through to the return case io.EOF: + wb.recordBytesRead() wb.span.End() default: wb.span.RecordError(err) @@ -184,7 +264,20 @@ func (wb *wrappedBody) Read(b []byte) (int, error) { return n, err } +// recordBytesRead is a function that ensures the number of bytes read is recorded once and only once. +func (wb *wrappedBody) recordBytesRead() { + // note: it is more performant (and equally correct) to use atomic.Bool over sync.Once here. In the event that + // two goroutines are racing to call this method, the number of bytes read will no longer increase. Using + // CompareAndSwap allows later goroutines to return quickly and not block waiting for the race winner to finish + // calling wb.record(wb.read.Load()). + if wb.recorded.CompareAndSwap(false, true) { + // Record the total number of bytes read + wb.record(wb.read.Load()) + } +} + func (wb *wrappedBody) Close() error { + wb.recordBytesRead() wb.span.End() if wb.body != nil { return wb.body.Close() diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go index 9a4a02143..7499f688b 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go @@ -16,7 +16,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http // Version is the current release version of the otelhttp instrumentation. func Version() string { - return "0.47.0" + return "0.48.0" // This string is updated by the pre_release.sh script during release } diff --git a/vendor/go.opentelemetry.io/contrib/zpages/version.go b/vendor/go.opentelemetry.io/contrib/zpages/version.go index 5f3c6dd81..00dfb0ce6 100644 --- a/vendor/go.opentelemetry.io/contrib/zpages/version.go +++ b/vendor/go.opentelemetry.io/contrib/zpages/version.go @@ -16,7 +16,7 @@ package zpages // import "go.opentelemetry.io/contrib/zpages" // Version is the current release version of the zpages span processor. func Version() string { - return "0.47.0" + return "0.48.0" // This string is updated by the pre_release.sh script during release } diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md index fe670d79c..5422ca7dc 100644 --- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -8,6 +8,60 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [1.23.1] 2024-02-07 + +### Fixed + +- Register all callbacks passed during observable instrument creation instead of just the last one multiple times in `go.opentelemetry.io/otel/sdk/metric`. (#4888) + +## [1.23.0] 2024-02-06 + +This release contains the first stable, `v1`, release of the following modules: + +- `go.opentelemetry.io/otel/bridge/opencensus` +- `go.opentelemetry.io/otel/bridge/opencensus/test` +- `go.opentelemetry.io/otel/example/opencensus` +- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` +- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` +- `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` + +See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. + +### Added + +- Add `WithEndpointURL` option to the `exporters/otlp/otlpmetric/otlpmetricgrpc`, `exporters/otlp/otlpmetric/otlpmetrichttp`, `exporters/otlp/otlptrace/otlptracegrpc` and `exporters/otlp/otlptrace/otlptracehttp` packages. (#4808) +- Experimental exemplar exporting is added to the metric SDK. + See [metric documentation](./sdk/metric/EXPERIMENTAL.md#exemplars) for more information about this feature and how to enable it. (#4871) +- `ErrSchemaURLConflict` is added to `go.opentelemetry.io/otel/sdk/resource`. + This error is returned when a merge of two `Resource`s with different (non-empty) schema URL is attempted. (#4876) + +### Changed + +- The `Merge` and `New` functions in `go.opentelemetry.io/otel/sdk/resource` now returns a partial result if there is a schema URL merge conflict. + Instead of returning `nil` when two `Resource`s with different (non-empty) schema URLs are merged the merged `Resource`, along with the new `ErrSchemaURLConflict` error, is returned. + It is up to the user to decide if they want to use the returned `Resource` or not. + It may have desired attributes overwritten or include stale semantic conventions. (#4876) + +### Fixed + +- Fix `ContainerID` resource detection on systemd when cgroup path has a colon. (#4449) +- Fix `go.opentelemetry.io/otel/sdk/metric` to cache instruments to avoid leaking memory when the same instrument is created multiple times. (#4820) +- Fix missing `Mix` and `Max` values for `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` by introducing `MarshalText` and `MarshalJSON` for the `Extrema` type in `go.opentelemetry.io/sdk/metric/metricdata`. (#4827) + +## [1.23.0-rc.1] 2024-01-18 + +This is a release candidate for the v1.23.0 release. +That release is expected to include the `v1` release of the following modules: + +- `go.opentelemetry.io/otel/bridge/opencensus` +- `go.opentelemetry.io/otel/bridge/opencensus/test` +- `go.opentelemetry.io/otel/example/opencensus` +- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` +- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` +- `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` + +See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. + ## [1.22.0/0.45.0] 2024-01-17 ### Added @@ -28,7 +82,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - Upgrade all use of `go.opentelemetry.io/otel/semconv` to use `v1.24.0`. (#4754) -- Update transformations in `go.opentelemetry.io/otel/exporters/zipkin` to follow `v1.19.0` version of the OpenTelemetry specification. (#4754) +- Update transformations in `go.opentelemetry.io/otel/exporters/zipkin` to follow `v1.24.0` version of the OpenTelemetry specification. (#4754) - Record synchronous measurements when the passed context is canceled instead of dropping in `go.opentelemetry.io/otel/sdk/metric`. If you do not want to make a measurement when the context is cancelled, you need to handle it yourself (e.g `if ctx.Err() != nil`). (#4671) - Improve `go.opentelemetry.io/otel/trace.TraceState`'s performance. (#4722) @@ -2775,7 +2829,10 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.22.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.23.1...HEAD +[1.23.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.1 +[1.23.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.0 +[1.23.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.0-rc.1 [1.22.0/0.45.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.22.0 [1.21.0/0.44.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.21.0 [1.20.0/0.43.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.20.0 diff --git a/vendor/go.opentelemetry.io/otel/CODEOWNERS b/vendor/go.opentelemetry.io/otel/CODEOWNERS index 623740007..31d336d92 100644 --- a/vendor/go.opentelemetry.io/otel/CODEOWNERS +++ b/vendor/go.opentelemetry.io/otel/CODEOWNERS @@ -14,4 +14,4 @@ * @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu -CODEOWNERS @MrAlias @MadVikingGod @pellared \ No newline at end of file +CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole \ No newline at end of file diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md index 31857a617..c9f2bac55 100644 --- a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -617,13 +617,13 @@ should be canceled. - [Evan Torrie](https://github.com/evantorrie), Verizon Media - [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics -- [David Ashpole](https://github.com/dashpole), Google - [Chester Cheung](https://github.com/hanyuancheung), Tencent - [Damien Mathieu](https://github.com/dmathieu), Elastic - [Anthony Mirabella](https://github.com/Aneurysm9), AWS ### Maintainers +- [David Ashpole](https://github.com/dashpole), Google - [Aaron Clawson](https://github.com/MadVikingGod), LightStep - [Robert Pająk](https://github.com/pellared), Splunk - [Tyler Yahn](https://github.com/MrAlias), Splunk diff --git a/vendor/go.opentelemetry.io/otel/attribute/set.go b/vendor/go.opentelemetry.io/otel/attribute/set.go index 7e6765b06..fb6da5145 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/set.go +++ b/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -427,7 +427,7 @@ func (l *Set) MarshalJSON() ([]byte, error) { return json.Marshal(l.equivalent.iface) } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Set. func (l Set) MarshalLog() interface{} { kvs := make(map[string]string) for _, kv := range l.ToSlice() { diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go index b46a38d60..cb41c7d58 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go @@ -104,7 +104,7 @@ func NewUnstarted(client Client) *Exporter { } } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Exporter. func (e *Exporter) MarshalLog() interface{} { return struct { Type string diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/doc.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/doc.go index 1f514ef9e..a3c2690c5 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/doc.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/doc.go @@ -28,7 +28,7 @@ The value may additionally a port, a scheme, and a path. The value accepts "http" and "https" scheme. The value should not contain a query string or fragment. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT. -The configuration can be overridden by [WithEndpoint], [WithInsecure], [WithGRPCConn] options. +The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options. OTEL_EXPORTER_OTLP_INSECURE, OTEL_EXPORTER_OTLP_TRACES_INSECURE (default: "false") - setting "true" disables client transport security for the exporter's gRPC connection. diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go index dddb1f334..f0203cbe7 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go @@ -20,6 +20,7 @@ package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ import ( "crypto/tls" "fmt" + "net/url" "path" "strings" "time" @@ -32,6 +33,7 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry" + "go.opentelemetry.io/otel/internal/global" ) const ( @@ -265,6 +267,24 @@ func WithEndpoint(endpoint string) GenericOption { }) } +func WithEndpointURL(v string) GenericOption { + return newGenericOption(func(cfg Config) Config { + u, err := url.Parse(v) + if err != nil { + global.Error(err, "otlptrace: parse endpoint url", "url", v) + return cfg + } + + cfg.Traces.Endpoint = u.Host + cfg.Traces.URLPath = u.Path + if u.Scheme != "https" { + cfg.Traces.Insecure = true + } + + return cfg + }) +} + func WithCompression(compression Compression) GenericOption { return newGenericOption(func(cfg Config) Config { cfg.Traces.Compression = compression diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go index 17ffeaf6e..0fddac758 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go @@ -64,14 +64,44 @@ func WithInsecure() Option { return wrappedOption{otlpconfig.WithInsecure()} } -// WithEndpoint sets the target endpoint the exporter will connect to. If -// unset, localhost:4317 will be used as a default. +// WithEndpointURL sets the target endpoint URL the Exporter will connect to. +// +// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +// environment variable is set, and this option is not passed, that variable +// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT +// will take precedence. +// +// If both this option and WithEndpointURL are used, the last used option will +// take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, "localhost:4317" will be used. // // This option has no effect if WithGRPCConn is used. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } +// WithEndpoint sets the target endpoint URL the Exporter will connect to. +// +// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +// environment variable is set, and this option is not passed, that variable +// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT +// will take precedence. +// +// If both this option and WithEndpoint are used, the last used option will +// take precedence. +// +// If an invalid URL is provided, the default value will be kept. +// +// By default, if an environment variable is not set, and this option is not +// passed, "localhost:4317" will be used. +// +// This option has no effect if WithGRPCConn is used. +func WithEndpointURL(u string) Option { + return wrappedOption{otlpconfig.WithEndpointURL(u)} +} + // WithReconnectionPeriod set the minimum amount of time between connection // attempts to the target endpoint. // diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go index 3b3bc3591..54cda3a16 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go @@ -16,5 +16,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" // Version is the current release version of the OpenTelemetry OTLP trace exporter in use. func Version() string { - return "1.22.0" + return "1.23.1" } diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go b/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go index 4279013be..aed756c5e 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go @@ -41,8 +41,20 @@ type Detector interface { // must never be done outside of a new major release. } -// Detect calls all input detectors sequentially and merges each result with the previous one. -// It returns the merged error too. +// Detect returns a new [Resource] merged from all the Resources each of the +// detectors produces. Each of the detectors are called sequentially, in the +// order they are passed, merging the produced resource into the previous. +// +// This may return a partial Resource along with an error containing +// [ErrPartialResource] if that error is returned from a detector. It may also +// return a merge-conflicting Resource along with an error containing +// [ErrSchemaURLConflict] if merging Resources from different detectors results +// in a schema URL conflict. It is up to the caller to determine if this +// returned Resource should be used or not. +// +// If one of the detectors returns an error that is not [ErrPartialResource], +// the resource produced by the detector will not be merged and the returned +// error will wrap that detector's error. func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { r := new(Resource) return r, detect(ctx, r, detectors) @@ -50,6 +62,10 @@ func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { // detect runs all detectors using ctx and merges the result into res. This // assumes res is allocated and not nil, it will panic otherwise. +// +// If the detectors or merging resources produces any errors (i.e. +// [ErrPartialResource] [ErrSchemaURLConflict]), a single error wrapping all of +// these errors will be returned. Otherwise, nil is returned. func detect(ctx context.Context, res *Resource, detectors []Detector) error { var ( r *Resource @@ -78,6 +94,11 @@ func detect(ctx context.Context, res *Resource, detectors []Detector) error { if len(errs) == 0 { return nil } + if errors.Is(errs, ErrSchemaURLConflict) { + // If there has been a merge conflict, ensure the resource has no + // schema URL. + res.schemaURL = "" + } return errs } diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go index c881b2895..c1b47193f 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -29,7 +29,7 @@ type containerIDProvider func() (string, error) var ( containerID containerIDProvider = getContainerIDFromCGroup - cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`) + cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*[-:])?([0-9a-f]+)(?:\.|\s*$)`) ) type cgroupContainerIDDetector struct{} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go index 176ff1066..cb1ee0a9c 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go @@ -17,6 +17,7 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "errors" + "fmt" "sync" "go.opentelemetry.io/otel" @@ -40,9 +41,20 @@ var ( defaultResourceOnce sync.Once ) -var errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflicting Schema URL") +// ErrSchemaURLConflict is an error returned when two Resources are merged +// together that contain different, non-empty, schema URLs. +var ErrSchemaURLConflict = errors.New("conflicting Schema URL") -// New returns a Resource combined from the user-provided detectors. +// New returns a [Resource] built using opts. +// +// This may return a partial Resource along with an error containing +// [ErrPartialResource] if options that provide a [Detector] are used and that +// error is returned from one or more of the Detectors. It may also return a +// merge-conflict Resource along with an error containing +// [ErrSchemaURLConflict] if merging Resources from the opts results in a +// schema URL conflict (see [Resource.Merge] for more information). It is up to +// the caller to determine if this returned Resource should be used or not +// based on these errors. func New(ctx context.Context, opts ...Option) (*Resource, error) { cfg := config{} for _, opt := range opts { @@ -98,7 +110,7 @@ func (r *Resource) String() string { return r.attrs.Encoded(attribute.DefaultEncoder()) } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Resource. func (r *Resource) MarshalLog() interface{} { return struct { Attributes attribute.Set @@ -146,16 +158,29 @@ func (r *Resource) Equal(eq *Resource) bool { return r.Equivalent() == eq.Equivalent() } -// Merge creates a new resource by combining resource a and b. +// Merge creates a new [Resource] by merging a and b. // -// If there are common keys between resource a and b, then the value -// from resource b will overwrite the value from resource a, even -// if resource b's value is empty. +// If there are common keys between a and b, then the value from b will +// overwrite the value from a, even if b's value is empty. // -// The SchemaURL of the resources will be merged according to the spec rules: -// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/resource/sdk.md#merge -// If the resources have different non-empty schemaURL an empty resource and an error -// will be returned. +// The SchemaURL of the resources will be merged according to the +// [OpenTelemetry specification rules]: +// +// - If a's schema URL is empty then the returned Resource's schema URL will +// be set to the schema URL of b, +// - Else if b's schema URL is empty then the returned Resource's schema URL +// will be set to the schema URL of a, +// - Else if the schema URLs of a and b are the same then that will be the +// schema URL of the returned Resource, +// - Else this is a merging error. If the resources have different, +// non-empty, schema URLs an error containing [ErrSchemaURLConflict] will +// be returned with the merged Resource. The merged Resource will have an +// empty schema URL. It may be the case that some unintended attributes +// have been overwritten or old semantic conventions persisted in the +// returned Resource. It is up to the caller to determine if this returned +// Resource should be used or not. +// +// [OpenTelemetry specification rules]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/resource/sdk.md#merge func Merge(a, b *Resource) (*Resource, error) { if a == nil && b == nil { return Empty(), nil @@ -167,19 +192,6 @@ func Merge(a, b *Resource) (*Resource, error) { return a, nil } - // Merge the schema URL. - var schemaURL string - switch true { - case a.schemaURL == "": - schemaURL = b.schemaURL - case b.schemaURL == "": - schemaURL = a.schemaURL - case a.schemaURL == b.schemaURL: - schemaURL = a.schemaURL - default: - return Empty(), errMergeConflictSchemaURL - } - // Note: 'b' attributes will overwrite 'a' with last-value-wins in attribute.Key() // Meaning this is equivalent to: append(a.Attributes(), b.Attributes()...) mi := attribute.NewMergeIterator(b.Set(), a.Set()) @@ -187,8 +199,23 @@ func Merge(a, b *Resource) (*Resource, error) { for mi.Next() { combine = append(combine, mi.Attribute()) } - merged := NewWithAttributes(schemaURL, combine...) - return merged, nil + + switch { + case a.schemaURL == "": + return NewWithAttributes(b.schemaURL, combine...), nil + case b.schemaURL == "": + return NewWithAttributes(a.schemaURL, combine...), nil + case a.schemaURL == b.schemaURL: + return NewWithAttributes(a.schemaURL, combine...), nil + } + // Return the merged resource with an appropriate error. It is up to + // the user to decide if the returned resource can be used or not. + return NewSchemaless(combine...), fmt.Errorf( + "%w: %s and %s", + ErrSchemaURLConflict, + a.schemaURL, + b.schemaURL, + ) } // Empty returns an instance of Resource with no attributes. It is diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go index c9c7effbf..fca26f2e7 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -406,7 +406,7 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) return false } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Span Processor. func (bsp *batchSpanProcessor) MarshalLog() interface{} { return struct { Type string diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go index 7d46c4b48..b1ac60846 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -55,7 +55,7 @@ type tracerProviderConfig struct { resource *resource.Resource } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Provider. func (cfg tracerProviderConfig) MarshalLog() interface{} { return struct { SpanProcessors []SpanProcessor diff --git a/vendor/go.opentelemetry.io/otel/sdk/version.go b/vendor/go.opentelemetry.io/otel/sdk/version.go index 17b16cc79..b6910a011 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/version.go +++ b/vendor/go.opentelemetry.io/otel/sdk/version.go @@ -16,5 +16,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk" // Version is the current release version of the OpenTelemetry SDK in use. func Version() string { - return "1.22.0" + return "1.23.1" } diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go index c7aba1c3f..0d998bcaf 100644 --- a/vendor/go.opentelemetry.io/otel/version.go +++ b/vendor/go.opentelemetry.io/otel/version.go @@ -16,5 +16,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.22.0" + return "1.23.1" } diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml index a9cfb80ae..19a73c626 100644 --- a/vendor/go.opentelemetry.io/otel/versions.yaml +++ b/vendor/go.opentelemetry.io/otel/versions.yaml @@ -14,19 +14,25 @@ module-sets: stable-v1: - version: v1.22.0 + version: v1.23.1 modules: - go.opentelemetry.io/otel + - go.opentelemetry.io/otel/bridge/opencensus + - go.opentelemetry.io/otel/bridge/opencensus/test - go.opentelemetry.io/otel/bridge/opentracing - go.opentelemetry.io/otel/bridge/opentracing/test - go.opentelemetry.io/otel/example/dice - go.opentelemetry.io/otel/example/namedtracer + - go.opentelemetry.io/otel/example/opencensus - go.opentelemetry.io/otel/example/otel-collector - go.opentelemetry.io/otel/example/passthrough - go.opentelemetry.io/otel/example/zipkin + - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc + - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp - go.opentelemetry.io/otel/exporters/otlp/otlptrace - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp + - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric - go.opentelemetry.io/otel/exporters/stdout/stdouttrace - go.opentelemetry.io/otel/exporters/zipkin - go.opentelemetry.io/otel/metric @@ -34,16 +40,10 @@ module-sets: - go.opentelemetry.io/otel/sdk/metric - go.opentelemetry.io/otel/trace experimental-metrics: - version: v0.45.0 + version: v0.45.2 modules: - - go.opentelemetry.io/otel/bridge/opencensus - - go.opentelemetry.io/otel/bridge/opencensus/test - - go.opentelemetry.io/otel/example/opencensus - go.opentelemetry.io/otel/example/prometheus - - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc - - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp - go.opentelemetry.io/otel/exporters/prometheus - - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric experimental-schema: version: v0.0.7 modules: diff --git a/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go b/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go index 51a499816..710908837 100644 --- a/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go +++ b/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go @@ -36,6 +36,69 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// SpanFlags represents constants used to interpret the +// Span.flags field, which is protobuf 'fixed32' type and is to +// be used as bit-fields. Each non-zero value defined in this enum is +// a bit-mask. To extract the bit-field, for example, use an +// expression like: +// +// (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) +// +// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. +// +// Note that Span flags were introduced in version 1.1 of the +// OpenTelemetry protocol. Older Span producers do not set this +// field, consequently consumers should not rely on the absence of a +// particular flag bit to indicate the presence of a particular feature. +type SpanFlags int32 + +const ( + // The zero value for the enum. Should not be used for comparisons. + // Instead use bitwise "and" with the appropriate mask as shown above. + SpanFlags_SPAN_FLAGS_DO_NOT_USE SpanFlags = 0 + // Bits 0-7 are used for trace flags. + SpanFlags_SPAN_FLAGS_TRACE_FLAGS_MASK SpanFlags = 255 +) + +// Enum value maps for SpanFlags. +var ( + SpanFlags_name = map[int32]string{ + 0: "SPAN_FLAGS_DO_NOT_USE", + 255: "SPAN_FLAGS_TRACE_FLAGS_MASK", + } + SpanFlags_value = map[string]int32{ + "SPAN_FLAGS_DO_NOT_USE": 0, + "SPAN_FLAGS_TRACE_FLAGS_MASK": 255, + } +) + +func (x SpanFlags) Enum() *SpanFlags { + p := new(SpanFlags) + *p = x + return p +} + +func (x SpanFlags) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SpanFlags) Descriptor() protoreflect.EnumDescriptor { + return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0].Descriptor() +} + +func (SpanFlags) Type() protoreflect.EnumType { + return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0] +} + +func (x SpanFlags) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SpanFlags.Descriptor instead. +func (SpanFlags) EnumDescriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{0} +} + // SpanKind is the type of span. Can be used to specify additional relationships between spans // in addition to a parent/child relationship. type Span_SpanKind int32 @@ -94,11 +157,11 @@ func (x Span_SpanKind) String() string { } func (Span_SpanKind) Descriptor() protoreflect.EnumDescriptor { - return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0].Descriptor() + return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1].Descriptor() } func (Span_SpanKind) Type() protoreflect.EnumType { - return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0] + return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1] } func (x Span_SpanKind) Number() protoreflect.EnumNumber { @@ -149,11 +212,11 @@ func (x Status_StatusCode) String() string { } func (Status_StatusCode) Descriptor() protoreflect.EnumDescriptor { - return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1].Descriptor() + return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[2].Descriptor() } func (Status_StatusCode) Type() protoreflect.EnumType { - return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1] + return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[2] } func (x Status_StatusCode) Number() protoreflect.EnumNumber { @@ -238,6 +301,9 @@ type ResourceSpans struct { Resource *v1.Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` // A list of ScopeSpans that originate from a resource. ScopeSpans []*ScopeSpans `protobuf:"bytes,2,rep,name=scope_spans,json=scopeSpans,proto3" json:"scope_spans,omitempty"` + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url // This schema_url applies to the data in the "resource" field. It does not apply // to the data in the "scope_spans" field which have their own schema_url field. SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"` @@ -308,6 +374,9 @@ type ScopeSpans struct { Scope *v11.InstrumentationScope `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"` // A list of Spans that originate from an instrumentation scope. Spans []*Span `protobuf:"bytes,2,rep,name=spans,proto3" json:"spans,omitempty"` + // The Schema URL, if known. This is the identifier of the Schema that the span data + // is recorded in. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url // This schema_url applies to all spans and span events in the "spans" field. SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"` } @@ -394,6 +463,21 @@ type Span struct { // The `span_id` of this span's parent span. If this is a root span, then this // field must be empty. The ID is an 8-byte array. ParentSpanId []byte `protobuf:"bytes,4,opt,name=parent_span_id,json=parentSpanId,proto3" json:"parent_span_id,omitempty"` + // Flags, a bit field. 8 least significant bits are the trace + // flags as defined in W3C Trace Context specification. Readers + // MUST not assume that 24 most significant bits will be zero. + // To read the 8-bit W3C trace flag, use `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // When creating span messages, if the message is logically forwarded from another source + // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + // be copied as-is. If creating from a source that does not have an equivalent flags field + // (such as a runtime representation of an OpenTelemetry span), the high 24 bits MUST + // be set to zero. + // + // [Optional]. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + Flags uint32 `protobuf:"fixed32,16,opt,name=flags,proto3" json:"flags,omitempty"` // A description of the span's operation. // // For example, the name can be a qualified method name or a file name @@ -517,6 +601,13 @@ func (x *Span) GetParentSpanId() []byte { return nil } +func (x *Span) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + func (x *Span) GetName() string { if x != nil { return x.Name @@ -757,6 +848,15 @@ type Span_Link struct { // dropped_attributes_count is the number of dropped attributes. If the value is 0, // then no attributes were dropped. DroppedAttributesCount uint32 `protobuf:"varint,5,opt,name=dropped_attributes_count,json=droppedAttributesCount,proto3" json:"dropped_attributes_count,omitempty"` + // Flags, a bit field. 8 least significant bits are the trace + // flags as defined in W3C Trace Context specification. Readers + // MUST not assume that 24 most significant bits will be zero. + // When creating new spans, the most-significant 24-bits MUST be + // zero. To read the 8-bit W3C trace flag (use flags & + // SPAN_FLAGS_TRACE_FLAGS_MASK). [Optional]. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + Flags uint32 `protobuf:"fixed32,6,opt,name=flags,proto3" json:"flags,omitempty"` } func (x *Span_Link) Reset() { @@ -826,6 +926,13 @@ func (x *Span_Link) GetDroppedAttributesCount() uint32 { return 0 } +func (x *Span_Link) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + var File_opentelemetry_proto_trace_v1_trace_proto protoreflect.FileDescriptor var file_opentelemetry_proto_trace_v1_trace_proto_rawDesc = []byte{ @@ -869,7 +976,7 @@ var file_opentelemetry_proto_trace_v1_trace_proto_rawDesc = []byte{ 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x55, 0x72, 0x6c, 0x22, 0x9c, 0x0a, 0x0a, 0x04, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x19, 0x0a, + 0x61, 0x55, 0x72, 0x6c, 0x22, 0xc8, 0x0a, 0x0a, 0x04, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, @@ -877,101 +984,108 @@ var file_opentelemetry_proto_trace_v1_trace_proto_rawDesc = []byte{ 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x53, - 0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x2f, 0x0a, - 0x14, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, - 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x06, 0x52, 0x11, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x2b, - 0x0a, 0x12, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, - 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x47, 0x0a, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x40, - 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, - 0x61, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x30, 0x0a, 0x14, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, - 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, - 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, - 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, - 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, - 0xc4, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x06, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, - 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, - 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, - 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0xde, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, - 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x70, - 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, - 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, - 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x99, 0x01, 0x0a, 0x08, 0x53, 0x70, 0x61, 0x6e, - 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x54, - 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x50, 0x41, 0x4e, 0x5f, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x14, 0x0a, - 0x10, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, - 0x54, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, - 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x45, 0x52, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x53, - 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x55, 0x4d, 0x45, - 0x52, 0x10, 0x05, 0x22, 0xbd, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x4e, 0x0a, - 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x54, - 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, - 0x45, 0x5f, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x4a, 0x04, 0x08, - 0x01, 0x10, 0x02, 0x42, 0x77, 0x0a, 0x1f, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, + 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x07, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x06, 0x52, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, + 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x2b, 0x0a, 0x12, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x06, + 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, + 0x6f, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, + 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, + 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, + 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0b, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, + 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0xaa, 0x02, 0x1c, - 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x72, 0x6f, 0x70, 0x70, + 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x4c, 0x69, 0x6e, + 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0xc4, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x24, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, + 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, + 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0xf4, 0x01, 0x0a, + 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x17, 0x0a, 0x07, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, + 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x07, 0x52, 0x05, 0x66, 0x6c, + 0x61, 0x67, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x08, 0x53, 0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, 0x64, + 0x12, 0x19, 0x0a, 0x15, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, + 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, + 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, + 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x50, 0x41, + 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, + 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x4f, + 0x44, 0x55, 0x43, 0x45, 0x52, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x55, 0x4d, 0x45, 0x52, 0x10, 0x05, 0x22, + 0xbd, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, + 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x4e, 0x0a, 0x0a, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x54, 0x10, 0x00, 0x12, 0x12, + 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4f, 0x4b, + 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x2a, + 0x48, 0x0a, 0x09, 0x53, 0x70, 0x61, 0x6e, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x15, + 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x46, 0x4c, 0x41, 0x47, 0x53, 0x5f, 0x44, 0x4f, 0x5f, 0x4e, 0x4f, + 0x54, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1b, 0x53, 0x50, 0x41, 0x4e, 0x5f, + 0x46, 0x4c, 0x41, 0x47, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x5f, 0x46, 0x4c, 0x41, 0x47, + 0x53, 0x5f, 0x4d, 0x41, 0x53, 0x4b, 0x10, 0xff, 0x01, 0x42, 0x77, 0x0a, 0x1f, 0x69, 0x6f, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x72, + 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x6f, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2f, 0x76, 0x31, 0xaa, 0x02, 0x1c, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -986,36 +1100,37 @@ func file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP() []byte { return file_opentelemetry_proto_trace_v1_trace_proto_rawDescData } -var file_opentelemetry_proto_trace_v1_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_opentelemetry_proto_trace_v1_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_opentelemetry_proto_trace_v1_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_opentelemetry_proto_trace_v1_trace_proto_goTypes = []interface{}{ - (Span_SpanKind)(0), // 0: opentelemetry.proto.trace.v1.Span.SpanKind - (Status_StatusCode)(0), // 1: opentelemetry.proto.trace.v1.Status.StatusCode - (*TracesData)(nil), // 2: opentelemetry.proto.trace.v1.TracesData - (*ResourceSpans)(nil), // 3: opentelemetry.proto.trace.v1.ResourceSpans - (*ScopeSpans)(nil), // 4: opentelemetry.proto.trace.v1.ScopeSpans - (*Span)(nil), // 5: opentelemetry.proto.trace.v1.Span - (*Status)(nil), // 6: opentelemetry.proto.trace.v1.Status - (*Span_Event)(nil), // 7: opentelemetry.proto.trace.v1.Span.Event - (*Span_Link)(nil), // 8: opentelemetry.proto.trace.v1.Span.Link - (*v1.Resource)(nil), // 9: opentelemetry.proto.resource.v1.Resource - (*v11.InstrumentationScope)(nil), // 10: opentelemetry.proto.common.v1.InstrumentationScope - (*v11.KeyValue)(nil), // 11: opentelemetry.proto.common.v1.KeyValue + (SpanFlags)(0), // 0: opentelemetry.proto.trace.v1.SpanFlags + (Span_SpanKind)(0), // 1: opentelemetry.proto.trace.v1.Span.SpanKind + (Status_StatusCode)(0), // 2: opentelemetry.proto.trace.v1.Status.StatusCode + (*TracesData)(nil), // 3: opentelemetry.proto.trace.v1.TracesData + (*ResourceSpans)(nil), // 4: opentelemetry.proto.trace.v1.ResourceSpans + (*ScopeSpans)(nil), // 5: opentelemetry.proto.trace.v1.ScopeSpans + (*Span)(nil), // 6: opentelemetry.proto.trace.v1.Span + (*Status)(nil), // 7: opentelemetry.proto.trace.v1.Status + (*Span_Event)(nil), // 8: opentelemetry.proto.trace.v1.Span.Event + (*Span_Link)(nil), // 9: opentelemetry.proto.trace.v1.Span.Link + (*v1.Resource)(nil), // 10: opentelemetry.proto.resource.v1.Resource + (*v11.InstrumentationScope)(nil), // 11: opentelemetry.proto.common.v1.InstrumentationScope + (*v11.KeyValue)(nil), // 12: opentelemetry.proto.common.v1.KeyValue } var file_opentelemetry_proto_trace_v1_trace_proto_depIdxs = []int32{ - 3, // 0: opentelemetry.proto.trace.v1.TracesData.resource_spans:type_name -> opentelemetry.proto.trace.v1.ResourceSpans - 9, // 1: opentelemetry.proto.trace.v1.ResourceSpans.resource:type_name -> opentelemetry.proto.resource.v1.Resource - 4, // 2: opentelemetry.proto.trace.v1.ResourceSpans.scope_spans:type_name -> opentelemetry.proto.trace.v1.ScopeSpans - 10, // 3: opentelemetry.proto.trace.v1.ScopeSpans.scope:type_name -> opentelemetry.proto.common.v1.InstrumentationScope - 5, // 4: opentelemetry.proto.trace.v1.ScopeSpans.spans:type_name -> opentelemetry.proto.trace.v1.Span - 0, // 5: opentelemetry.proto.trace.v1.Span.kind:type_name -> opentelemetry.proto.trace.v1.Span.SpanKind - 11, // 6: opentelemetry.proto.trace.v1.Span.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue - 7, // 7: opentelemetry.proto.trace.v1.Span.events:type_name -> opentelemetry.proto.trace.v1.Span.Event - 8, // 8: opentelemetry.proto.trace.v1.Span.links:type_name -> opentelemetry.proto.trace.v1.Span.Link - 6, // 9: opentelemetry.proto.trace.v1.Span.status:type_name -> opentelemetry.proto.trace.v1.Status - 1, // 10: opentelemetry.proto.trace.v1.Status.code:type_name -> opentelemetry.proto.trace.v1.Status.StatusCode - 11, // 11: opentelemetry.proto.trace.v1.Span.Event.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue - 11, // 12: opentelemetry.proto.trace.v1.Span.Link.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 4, // 0: opentelemetry.proto.trace.v1.TracesData.resource_spans:type_name -> opentelemetry.proto.trace.v1.ResourceSpans + 10, // 1: opentelemetry.proto.trace.v1.ResourceSpans.resource:type_name -> opentelemetry.proto.resource.v1.Resource + 5, // 2: opentelemetry.proto.trace.v1.ResourceSpans.scope_spans:type_name -> opentelemetry.proto.trace.v1.ScopeSpans + 11, // 3: opentelemetry.proto.trace.v1.ScopeSpans.scope:type_name -> opentelemetry.proto.common.v1.InstrumentationScope + 6, // 4: opentelemetry.proto.trace.v1.ScopeSpans.spans:type_name -> opentelemetry.proto.trace.v1.Span + 1, // 5: opentelemetry.proto.trace.v1.Span.kind:type_name -> opentelemetry.proto.trace.v1.Span.SpanKind + 12, // 6: opentelemetry.proto.trace.v1.Span.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 8, // 7: opentelemetry.proto.trace.v1.Span.events:type_name -> opentelemetry.proto.trace.v1.Span.Event + 9, // 8: opentelemetry.proto.trace.v1.Span.links:type_name -> opentelemetry.proto.trace.v1.Span.Link + 7, // 9: opentelemetry.proto.trace.v1.Span.status:type_name -> opentelemetry.proto.trace.v1.Status + 2, // 10: opentelemetry.proto.trace.v1.Status.code:type_name -> opentelemetry.proto.trace.v1.Status.StatusCode + 12, // 11: opentelemetry.proto.trace.v1.Span.Event.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 12, // 12: opentelemetry.proto.trace.v1.Span.Link.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue 13, // [13:13] is the sub-list for method output_type 13, // [13:13] is the sub-list for method input_type 13, // [13:13] is the sub-list for extension type_name @@ -1119,7 +1234,7 @@ func file_opentelemetry_proto_trace_v1_trace_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_opentelemetry_proto_trace_v1_trace_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 7, NumExtensions: 0, NumServices: 0, diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go index de67f938a..3c57880d6 100644 --- a/vendor/golang.org/x/net/html/token.go +++ b/vendor/golang.org/x/net/html/token.go @@ -910,9 +910,6 @@ func (z *Tokenizer) readTagAttrKey() { return } switch c { - case ' ', '\n', '\r', '\t', '\f', '/': - z.pendingAttr[0].end = z.raw.end - 1 - return case '=': if z.pendingAttr[0].start+1 == z.raw.end { // WHATWG 13.2.5.32, if we see an equals sign before the attribute name @@ -920,7 +917,9 @@ func (z *Tokenizer) readTagAttrKey() { continue } fallthrough - case '>': + case ' ', '\n', '\r', '\t', '\f', '/', '>': + // WHATWG 13.2.5.33 Attribute name state + // We need to reconsume the char in the after attribute name state to support the / character z.raw.end-- z.pendingAttr[0].end = z.raw.end return @@ -939,6 +938,11 @@ func (z *Tokenizer) readTagAttrVal() { if z.err != nil { return } + if c == '/' { + // WHATWG 13.2.5.34 After attribute name state + // U+002F SOLIDUS (/) - Switch to the self-closing start tag state. + return + } if c != '=' { z.raw.end-- return diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index c1f6b90dc..e2b298d85 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -1510,13 +1510,12 @@ func (mh *MetaHeadersFrame) checkPseudos() error { } func (fr *Framer) maxHeaderStringLen() int { - v := fr.maxHeaderListSize() - if uint32(int(v)) == v { - return int(v) + v := int(fr.maxHeaderListSize()) + if v < 0 { + // If maxHeaderListSize overflows an int, use no limit (0). + return 0 } - // They had a crazy big number for MaxHeaderBytes anyway, - // so give them unlimited header lengths: - return 0 + return v } // readMetaFrame returns 0 or more CONTINUATION frames from fr and diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index c6492020e..fdcaa974d 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -584,7 +584,7 @@ ccflags="$@" $2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ || $2 ~ /^KEYCTL_/ || $2 ~ /^PERF_/ || - $2 ~ /^SECCOMP_MODE_/ || + $2 ~ /^SECCOMP_/ || $2 ~ /^SEEK_/ || $2 ~ /^SCHED_/ || $2 ~ /^SPLICE_/ || diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index a5d3ff8df..36bf8399f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1785,6 +1785,8 @@ const ( LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20 LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000 LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2 + LANDLOCK_ACCESS_NET_BIND_TCP = 0x1 + LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2 LANDLOCK_CREATE_RULESET_VERSION = 0x1 LINUX_REBOOT_CMD_CAD_OFF = 0x0 LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef @@ -2465,6 +2467,7 @@ const ( PR_MCE_KILL_GET = 0x22 PR_MCE_KILL_LATE = 0x0 PR_MCE_KILL_SET = 0x1 + PR_MDWE_NO_INHERIT = 0x2 PR_MDWE_REFUSE_EXEC_GAIN = 0x1 PR_MPX_DISABLE_MANAGEMENT = 0x2c PR_MPX_ENABLE_MANAGEMENT = 0x2b @@ -2669,8 +2672,9 @@ const ( RTAX_FEATURES = 0xc RTAX_FEATURE_ALLFRAG = 0x8 RTAX_FEATURE_ECN = 0x1 - RTAX_FEATURE_MASK = 0xf + RTAX_FEATURE_MASK = 0x1f RTAX_FEATURE_SACK = 0x2 + RTAX_FEATURE_TCP_USEC_TS = 0x10 RTAX_FEATURE_TIMESTAMP = 0x4 RTAX_HOPLIMIT = 0xa RTAX_INITCWND = 0xb @@ -2913,9 +2917,38 @@ const ( SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x1d SC_LOG_FLUSH = 0x100000 + SECCOMP_ADDFD_FLAG_SEND = 0x2 + SECCOMP_ADDFD_FLAG_SETFD = 0x1 + SECCOMP_FILTER_FLAG_LOG = 0x2 + SECCOMP_FILTER_FLAG_NEW_LISTENER = 0x8 + SECCOMP_FILTER_FLAG_SPEC_ALLOW = 0x4 + SECCOMP_FILTER_FLAG_TSYNC = 0x1 + SECCOMP_FILTER_FLAG_TSYNC_ESRCH = 0x10 + SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV = 0x20 + SECCOMP_GET_ACTION_AVAIL = 0x2 + SECCOMP_GET_NOTIF_SIZES = 0x3 + SECCOMP_IOCTL_NOTIF_RECV = 0xc0502100 + SECCOMP_IOCTL_NOTIF_SEND = 0xc0182101 + SECCOMP_IOC_MAGIC = '!' SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 + SECCOMP_RET_ACTION = 0x7fff0000 + SECCOMP_RET_ACTION_FULL = 0xffff0000 + SECCOMP_RET_ALLOW = 0x7fff0000 + SECCOMP_RET_DATA = 0xffff + SECCOMP_RET_ERRNO = 0x50000 + SECCOMP_RET_KILL = 0x0 + SECCOMP_RET_KILL_PROCESS = 0x80000000 + SECCOMP_RET_KILL_THREAD = 0x0 + SECCOMP_RET_LOG = 0x7ffc0000 + SECCOMP_RET_TRACE = 0x7ff00000 + SECCOMP_RET_TRAP = 0x30000 + SECCOMP_RET_USER_NOTIF = 0x7fc00000 + SECCOMP_SET_MODE_FILTER = 0x1 + SECCOMP_SET_MODE_STRICT = 0x0 + SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP = 0x1 + SECCOMP_USER_NOTIF_FLAG_CONTINUE = 0x1 SECRETMEM_MAGIC = 0x5345434d SECURITYFS_MAGIC = 0x73636673 SEEK_CUR = 0x1 @@ -3075,6 +3108,7 @@ const ( SOL_TIPC = 0x10f SOL_TLS = 0x11a SOL_UDP = 0x11 + SOL_VSOCK = 0x11f SOL_X25 = 0x106 SOL_XDP = 0x11b SOMAXCONN = 0x1000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 4920821cf..42ff8c3c1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -281,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index a0c1e4112..dca436004 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -282,6 +282,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index c63985560..5cca668ac 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -288,6 +288,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 47cc62e25..d8cae6d15 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -278,6 +278,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 27ac4a09e..28e39afdc 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -275,6 +275,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 54694642a..cd66e92cb 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -281,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 3adb81d75..c1595eba7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -281,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 2dfe98f0d..ee9456b0d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -281,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index f5398f84f..8cfca81e1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -281,6 +281,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x80 SIOCATMARK = 0x40047307 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index c54f152d6..60b0deb3a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -336,6 +336,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 76057dc72..f90aa7281 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -340,6 +340,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index e0c3725e2..ba9e01503 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -340,6 +340,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 18f2813ed..07cdfd6e9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -272,6 +272,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 11619d4ec..2f1dd214a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -344,6 +344,9 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104 SFD_CLOEXEC = 0x80000 SFD_NONBLOCK = 0x800 SIOCATMARK = 0x8905 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 396d994da..f40519d90 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -335,6 +335,9 @@ const ( SCM_TIMESTAMPNS = 0x21 SCM_TXTIME = 0x3f SCM_WIFI_STATUS = 0x25 + SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 + SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102 + SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104 SFD_CLOEXEC = 0x400000 SFD_NONBLOCK = 0x4000 SF_FP = 0x38 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index fcf3ecbdd..0cc3ce496 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -448,4 +448,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index f56dc2504..856d92d69 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -371,4 +371,7 @@ const ( SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index 974bf2467..8d467094c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -412,4 +412,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 39a2739e2..edc173244 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -315,4 +315,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index cf9c9d77e..445eba206 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -309,4 +309,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 10b7362ef..adba01bca 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -432,4 +432,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 SYS_FCHMODAT2 = 4452 + SYS_MAP_SHADOW_STACK = 4453 + SYS_FUTEX_WAKE = 4454 + SYS_FUTEX_WAIT = 4455 + SYS_FUTEX_REQUEUE = 4456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index cd4d8b4fd..014c4e9c7 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -362,4 +362,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 SYS_FCHMODAT2 = 5452 + SYS_MAP_SHADOW_STACK = 5453 + SYS_FUTEX_WAKE = 5454 + SYS_FUTEX_WAIT = 5455 + SYS_FUTEX_REQUEUE = 5456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index 2c0efca81..ccc97d74d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -362,4 +362,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 SYS_FCHMODAT2 = 5452 + SYS_MAP_SHADOW_STACK = 5453 + SYS_FUTEX_WAKE = 5454 + SYS_FUTEX_WAIT = 5455 + SYS_FUTEX_REQUEUE = 5456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index a72e31d39..ec2b64a95 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -432,4 +432,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 SYS_FCHMODAT2 = 4452 + SYS_MAP_SHADOW_STACK = 4453 + SYS_FUTEX_WAKE = 4454 + SYS_FUTEX_WAIT = 4455 + SYS_FUTEX_REQUEUE = 4456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index c7d1e3747..21a839e33 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -439,4 +439,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index f4d4838c8..c11121ec3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -411,4 +411,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index b64f0e591..909b631fc 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -411,4 +411,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 95711195a..e49bed16e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -316,4 +316,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index f94e943bc..66017d2d3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -377,4 +377,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index ba0c2bc51..47bab18dc 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -390,4 +390,8 @@ const ( SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 + SYS_FUTEX_WAKE = 454 + SYS_FUTEX_WAIT = 455 + SYS_FUTEX_REQUEUE = 456 ) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index bbf8399ff..dc0c955ee 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -174,7 +174,8 @@ type FscryptPolicyV2 struct { Contents_encryption_mode uint8 Filenames_encryption_mode uint8 Flags uint8 - _ [4]uint8 + Log2_data_unit_size uint8 + _ [3]uint8 Master_key_identifier [16]uint8 } @@ -455,60 +456,63 @@ type Ucred struct { } type TCPInfo struct { - State uint8 - Ca_state uint8 - Retransmits uint8 - Probes uint8 - Backoff uint8 - Options uint8 - Rto uint32 - Ato uint32 - Snd_mss uint32 - Rcv_mss uint32 - Unacked uint32 - Sacked uint32 - Lost uint32 - Retrans uint32 - Fackets uint32 - Last_data_sent uint32 - Last_ack_sent uint32 - Last_data_recv uint32 - Last_ack_recv uint32 - Pmtu uint32 - Rcv_ssthresh uint32 - Rtt uint32 - Rttvar uint32 - Snd_ssthresh uint32 - Snd_cwnd uint32 - Advmss uint32 - Reordering uint32 - Rcv_rtt uint32 - Rcv_space uint32 - Total_retrans uint32 - Pacing_rate uint64 - Max_pacing_rate uint64 - Bytes_acked uint64 - Bytes_received uint64 - Segs_out uint32 - Segs_in uint32 - Notsent_bytes uint32 - Min_rtt uint32 - Data_segs_in uint32 - Data_segs_out uint32 - Delivery_rate uint64 - Busy_time uint64 - Rwnd_limited uint64 - Sndbuf_limited uint64 - Delivered uint32 - Delivered_ce uint32 - Bytes_sent uint64 - Bytes_retrans uint64 - Dsack_dups uint32 - Reord_seen uint32 - Rcv_ooopack uint32 - Snd_wnd uint32 - Rcv_wnd uint32 - Rehash uint32 + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 + Pacing_rate uint64 + Max_pacing_rate uint64 + Bytes_acked uint64 + Bytes_received uint64 + Segs_out uint32 + Segs_in uint32 + Notsent_bytes uint32 + Min_rtt uint32 + Data_segs_in uint32 + Data_segs_out uint32 + Delivery_rate uint64 + Busy_time uint64 + Rwnd_limited uint64 + Sndbuf_limited uint64 + Delivered uint32 + Delivered_ce uint32 + Bytes_sent uint64 + Bytes_retrans uint64 + Dsack_dups uint32 + Reord_seen uint32 + Rcv_ooopack uint32 + Snd_wnd uint32 + Rcv_wnd uint32 + Rehash uint32 + Total_rto uint16 + Total_rto_recoveries uint16 + Total_rto_time uint32 } type CanFilter struct { @@ -551,7 +555,7 @@ const ( SizeofIPv6MTUInfo = 0x20 SizeofICMPv6Filter = 0x20 SizeofUcred = 0xc - SizeofTCPInfo = 0xf0 + SizeofTCPInfo = 0xf8 SizeofCanFilter = 0x8 SizeofTCPRepairOpt = 0x8 ) @@ -3399,7 +3403,7 @@ const ( DEVLINK_PORT_FN_ATTR_STATE = 0x2 DEVLINK_PORT_FN_ATTR_OPSTATE = 0x3 DEVLINK_PORT_FN_ATTR_CAPS = 0x4 - DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x4 + DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x5 ) type FsverityDigest struct { @@ -4183,7 +4187,8 @@ const ( ) type LandlockRulesetAttr struct { - Access_fs uint64 + Access_fs uint64 + Access_net uint64 } type LandlockPathBeneathAttr struct { @@ -5134,7 +5139,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x1b + NL80211_FREQUENCY_ATTR_MAX = 0x1c NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc @@ -5547,7 +5552,7 @@ const ( NL80211_REGDOM_TYPE_CUSTOM_WORLD = 0x2 NL80211_REGDOM_TYPE_INTERSECTION = 0x3 NL80211_REGDOM_TYPE_WORLD = 0x1 - NL80211_REG_RULE_ATTR_MAX = 0x7 + NL80211_REG_RULE_ATTR_MAX = 0x8 NL80211_REKEY_DATA_AKM = 0x4 NL80211_REKEY_DATA_KCK = 0x2 NL80211_REKEY_DATA_KEK = 0x1 diff --git a/vendor/golang.org/x/sys/windows/env_windows.go b/vendor/golang.org/x/sys/windows/env_windows.go index b8ad19250..d4577a423 100644 --- a/vendor/golang.org/x/sys/windows/env_windows.go +++ b/vendor/golang.org/x/sys/windows/env_windows.go @@ -37,14 +37,17 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) { return nil, err } defer DestroyEnvironmentBlock(block) - blockp := unsafe.Pointer(block) - for { - entry := UTF16PtrToString((*uint16)(blockp)) - if len(entry) == 0 { - break + size := unsafe.Sizeof(*block) + for *block != 0 { + // find NUL terminator + end := unsafe.Pointer(block) + for *(*uint16)(end) != 0 { + end = unsafe.Add(end, size) } - env = append(env, entry) - blockp = unsafe.Add(blockp, 2*(len(entry)+1)) + + entry := unsafe.Slice(block, (uintptr(end)-uintptr(unsafe.Pointer(block)))/size) + env = append(env, UTF16ToString(entry)) + block = (*uint16)(unsafe.Add(end, size)) } return env, nil } diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index ffb8708cc..6395a031d 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -125,8 +125,7 @@ func UTF16PtrToString(p *uint16) string { for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ { ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p)) } - - return string(utf16.Decode(unsafe.Slice(p, n))) + return UTF16ToString(unsafe.Slice(p, n)) } func Getpagesize() int { return 4096 } diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index b330ccedc..83c382982 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -535,8 +535,8 @@ const minBatchSize = 1000 // size is too low to give stream goroutines a chance to fill it up. // // Upon exiting, if the error causing the exit is not an I/O error, run() -// flushes and closes the underlying connection. Otherwise, the connection is -// left open to allow the I/O error to be encountered by the reader instead. +// flushes the underlying connection. The connection is always left open to +// allow different closing behavior on the client and server. func (l *loopyWriter) run() (err error) { defer func() { if l.logger.V(logLevel) { @@ -544,7 +544,6 @@ func (l *loopyWriter) run() (err error) { } if !isIOError(err) { l.framer.writer.Flush() - l.conn.Close() } l.cbuf.finish() }() diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index c33ac5961..eff879964 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -451,7 +451,13 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } go func() { t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) - t.loopy.run() + if err := t.loopy.run(); !isIOError(err) { + // Immediately close the connection, as the loopy writer returns + // when there are no more active streams and we were draining (the + // server sent a GOAWAY). For I/O errors, the reader will hit it + // after draining any remaining incoming data. + t.conn.Close() + } close(t.writerDone) }() return t, nil diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index f6bac0e8a..a206e2eef 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -322,8 +322,24 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, go func() { t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler - t.loopy.run() + err := t.loopy.run() close(t.loopyWriterDone) + if !isIOError(err) { + // Close the connection if a non-I/O error occurs (for I/O errors + // the reader will also encounter the error and close). Wait 1 + // second before closing the connection, or when the reader is done + // (i.e. the client already closed the connection or a connection + // error occurred). This avoids the potential problem where there + // is unread data on the receive side of the connection, which, if + // closed, would lead to a TCP RST instead of FIN, and the client + // encountering errors. For more info: + // https://github.com/grpc/grpc-go/issues/5358 + select { + case <-t.readerDone: + case <-time.After(time.Second): + } + t.conn.Close() + } }() go t.keepalive() return t, nil @@ -609,8 +625,8 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade // traceCtx attaches trace to ctx and returns the new context. func (t *http2Server) HandleStreams(ctx context.Context, handle func(*Stream)) { defer func() { - <-t.loopyWriterDone close(t.readerDone) + <-t.loopyWriterDone }() for { t.controlBuf.throttle() @@ -1329,6 +1345,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil { return false, err } + t.framer.writer.Flush() if retErr != nil { return false, retErr } @@ -1349,7 +1366,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { return false, err } go func() { - timer := time.NewTimer(time.Minute) + timer := time.NewTimer(5 * time.Second) defer timer.Stop() select { case <-t.drainEvent.Done(): diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 1ad1ba2ad..f1aec4c0a 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.61.0" +const Version = "1.61.1" diff --git a/vendor/modules.txt b/vendor/modules.txt index 79cd2bad9..5cdac4973 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -362,7 +362,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.18.1-0.20240129131717-cff0a2eeb959 +# github.com/cs3org/reva/v2 v2.18.1-0.20240208105019-d5e420d294be ## explicit; go 1.21 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime @@ -658,6 +658,10 @@ github.com/cs3org/reva/v2/pkg/storage/fs/ocis github.com/cs3org/reva/v2/pkg/storage/fs/ocis/blobstore github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/filecache +github.com/cs3org/reva/v2/pkg/storage/fs/posix +github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore +github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup +github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree github.com/cs3org/reva/v2/pkg/storage/fs/registry github.com/cs3org/reva/v2/pkg/storage/fs/s3 github.com/cs3org/reva/v2/pkg/storage/fs/s3ng @@ -670,6 +674,7 @@ github.com/cs3org/reva/v2/pkg/storage/utils/ace github.com/cs3org/reva/v2/pkg/storage/utils/acl github.com/cs3org/reva/v2/pkg/storage/utils/chunking github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs +github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes @@ -677,6 +682,7 @@ github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/migrator github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/mtimesyncedcache github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options +github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaceidindex github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/propagator @@ -974,7 +980,7 @@ github.com/go-playground/locales/en # github.com/go-playground/universal-translator v0.18.1 ## explicit; go 1.18 github.com/go-playground/universal-translator -# github.com/go-playground/validator/v10 v10.17.0 +# github.com/go-playground/validator/v10 v10.18.0 ## explicit; go 1.18 github.com/go-playground/validator/v10 github.com/go-playground/validator/v10/translations/en @@ -1235,16 +1241,18 @@ github.com/justinas/alice # github.com/kevinburke/ssh_config v1.2.0 ## explicit github.com/kevinburke/ssh_config -# github.com/klauspost/compress v1.17.4 +# github.com/klauspost/compress v1.17.5 ## explicit; go 1.19 github.com/klauspost/compress/flate +github.com/klauspost/compress/internal/race github.com/klauspost/compress/s2 # github.com/klauspost/cpuid/v2 v2.1.0 ## explicit; go 1.15 github.com/klauspost/cpuid/v2 -# github.com/leodido/go-urn v1.2.4 -## explicit; go 1.16 +# github.com/leodido/go-urn v1.4.0 +## explicit; go 1.18 github.com/leodido/go-urn +github.com/leodido/go-urn/scim/schema # github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 ## explicit; go 1.13 github.com/leonelquinteros/gotext @@ -1392,7 +1400,7 @@ github.com/mschoch/smat # github.com/nats-io/jwt/v2 v2.5.3 ## explicit; go 1.18 github.com/nats-io/jwt/v2 -# github.com/nats-io/nats-server/v2 v2.10.9 +# github.com/nats-io/nats-server/v2 v2.10.10 ## explicit; go 1.20 github.com/nats-io/nats-server/v2/conf github.com/nats-io/nats-server/v2/internal/fastrand @@ -1403,6 +1411,7 @@ github.com/nats-io/nats-server/v2/server/avl github.com/nats-io/nats-server/v2/server/certidp github.com/nats-io/nats-server/v2/server/certstore github.com/nats-io/nats-server/v2/server/pse +github.com/nats-io/nats-server/v2/server/stree github.com/nats-io/nats-server/v2/server/sysmem # github.com/nats-io/nats.go v1.32.0 ## explicit; go 1.20 @@ -1677,7 +1686,7 @@ github.com/rs/cors # github.com/rs/xid v1.5.0 ## explicit; go 1.12 github.com/rs/xid -# github.com/rs/zerolog v1.31.0 +# github.com/rs/zerolog v1.32.0 ## explicit; go 1.15 github.com/rs/zerolog github.com/rs/zerolog/internal/cbor @@ -1926,15 +1935,15 @@ go.opentelemetry.io/contrib ## explicit; go 1.20 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal -# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 +# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 ## explicit; go 1.20 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil -# go.opentelemetry.io/contrib/zpages v0.47.0 +# go.opentelemetry.io/contrib/zpages v0.48.0 ## explicit; go 1.20 go.opentelemetry.io/contrib/zpages go.opentelemetry.io/contrib/zpages/internal -# go.opentelemetry.io/otel v1.22.0 +# go.opentelemetry.io/otel v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute @@ -1959,22 +1968,22 @@ go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift -# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel/exporters/otlp/otlptrace go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform -# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/envconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry -# go.opentelemetry.io/otel/metric v1.22.0 +# go.opentelemetry.io/otel/metric v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel/metric go.opentelemetry.io/otel/metric/embedded -# go.opentelemetry.io/otel/sdk v1.22.0 +# go.opentelemetry.io/otel/sdk v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/sdk/instrumentation @@ -1982,12 +1991,12 @@ go.opentelemetry.io/otel/sdk/internal go.opentelemetry.io/otel/sdk/internal/env go.opentelemetry.io/otel/sdk/resource go.opentelemetry.io/otel/sdk/trace -# go.opentelemetry.io/otel/trace v1.22.0 +# go.opentelemetry.io/otel/trace v1.23.1 ## explicit; go 1.20 go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/noop -# go.opentelemetry.io/proto/otlp v1.0.0 +# go.opentelemetry.io/proto/otlp v1.1.0 ## explicit; go 1.17 go.opentelemetry.io/proto/otlp/collector/trace/v1 go.opentelemetry.io/proto/otlp/common/v1 @@ -2009,7 +2018,7 @@ go.uber.org/zap/internal/color go.uber.org/zap/internal/exit go.uber.org/zap/zapcore go.uber.org/zap/zapgrpc -# golang.org/x/crypto v0.18.0 +# golang.org/x/crypto v0.19.0 ## explicit; go 1.18 golang.org/x/crypto/argon2 golang.org/x/crypto/bcrypt @@ -2059,7 +2068,7 @@ golang.org/x/image/vector golang.org/x/mod/internal/lazyregexp golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.20.0 +# golang.org/x/net v0.21.0 ## explicit; go 1.18 golang.org/x/net/bpf golang.org/x/net/context @@ -2082,7 +2091,7 @@ golang.org/x/net/netutil golang.org/x/net/proxy golang.org/x/net/publicsuffix golang.org/x/net/trace -# golang.org/x/oauth2 v0.16.0 +# golang.org/x/oauth2 v0.17.0 ## explicit; go 1.18 golang.org/x/oauth2 golang.org/x/oauth2/internal @@ -2091,7 +2100,7 @@ golang.org/x/oauth2/internal golang.org/x/sync/errgroup golang.org/x/sync/semaphore golang.org/x/sync/singleflight -# golang.org/x/sys v0.16.0 +# golang.org/x/sys v0.17.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -2102,7 +2111,7 @@ golang.org/x/sys/windows/registry golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/eventlog golang.org/x/sys/windows/svc/mgr -# golang.org/x/term v0.16.0 +# golang.org/x/term v0.17.0 ## explicit; go 1.18 golang.org/x/term # golang.org/x/text v0.14.0 @@ -2184,7 +2193,7 @@ google.golang.org/genproto/googleapis/api/httpbody ## explicit; go 1.19 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.61.0 +# google.golang.org/grpc v1.61.1 ## explicit; go 1.19 google.golang.org/grpc google.golang.org/grpc/attributes