Compare commits

..
Author SHA1 Message Date
Klaas Freitag 41a9ad809d Use inotifywait module from upstream 2026-06-09 15:39:21 +02:00
Ralf Haferkamp 0f97fc5e60 Merge pull request #2906 from opencloud-eu/fix/aarch64-idp-build
fix(idp): aarch64 build
2026-06-08 14:05:04 +02:00
Jannik Stehle f060ca4660 fix(idp): aarch64 build
Specifies the supported architectures to ensure that platform-specific
dependencies are fetched for every architecture we build/package for.
2026-06-08 11:11:14 +02:00
Ralf Haferkamp 3112d136ff Merge pull request #2883 from opencloud-eu/dependabot/npm_and_yarn/services/idp/axios-1.16.1
build(deps): bump axios from 1.16.0 to 1.16.1 in /services/idp
2026-06-08 09:27:52 +02:00
Ralf Haferkamp 4414bd0d08 Merge pull request #2879 from opencloud-eu/use-blessed-mapstructure-fork
switch to go-viper/mapstructure
2026-06-04 12:12:25 +02:00
Christian Richter 8593a1409a incorporate requested changes
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-06-03 16:28:44 +02:00
Christian Richter 6d9d2baa80 force ldap to listen only on localhost
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-06-03 16:28:44 +02:00
Christian Richter d6c6340705 use ldap instead of ldaps internally
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-06-03 16:28:44 +02:00
Ralf Haferkamp 9915fc8ea7 Merge pull request #2888 from dragonchaser/bump-reva
bump reva to 2.46.2
2026-06-03 11:49:28 +02:00
Christian Richter 6dff206e8a bump reva to 2.46.2
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-06-03 11:18:20 +02:00
dependabot[bot] a478f59c50 build(deps): bump axios from 1.16.0 to 1.16.1 in /services/idp
Bumps [axios](https://github.com/axios/axios) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.16.0...v1.16.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.16.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-02 19:57:24 +00:00
Jörn Friedrich Dreyer 48a9448ba7 switch to go-viper/mapstructure
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2026-06-02 13:57:37 +02:00
Michael Barz 83360f9c89 Merge pull request #2820 from opencloud-eu/next-release/main
🎉 Release 7.1.0
2026-06-02 12:12:02 +02:00
oauth b3b8461ea9 🎉 Release 7.1.0 2026-06-02 10:01:41 +00:00
oauth 4ba5af3ed8 Merge branch 'origin/main' into 'next-release/main' 2026-06-02 10:01:40 +00:00
Christian Richter 6e021e5d3f bump reva
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-06-02 11:59:12 +02:00
oauth 315256699a 🎉 Release 7.1.0 2026-06-01 18:43:27 +00:00
oauth f9fdff0705 Merge branch 'origin/main' into 'next-release/main' 2026-06-01 18:43:26 +00:00
Michael Barz 95423b6002 Merge pull request #2874 from opencloud-eu/bump-version
chore: bump minor version
2026-06-01 20:41:02 +02:00
Michael Barz 1b8393e9d3 chore: bump minor version 2026-06-01 17:06:47 +02:00
oauth 0cd1efabf8 🎉 Release 7.1.0 2026-06-01 14:53:20 +00:00
oauth b6223069df 🎉 Release 7.1.0 2026-06-01 13:25:32 +00:00
oauth bd931bd69c Merge branch 'origin/main' into 'next-release/main' 2026-06-01 13:25:31 +00:00
Michael Barz 41d0365903 Merge pull request #2870 from opencloud-eu/bump-web-7.1.0
chore: bump web to v7.1.0
2026-06-01 15:20:32 +02:00
Benedikt Kulmann aa2f5d9667 chore: bump web to v7.1.0 2026-06-01 14:18:16 +02:00
Ralf Haferkamp 60fc81c222 Merge pull request #2869 from rhafer/revabump
chore: bump reva to 2.46.1
2026-06-01 11:15:01 +02:00
oauth 31bb970de9 🎉 Release 7.0.1 2026-06-01 08:41:24 +00:00
oauth 5cf62ff817 Merge branch 'origin/main' into 'next-release/main' 2026-06-01 08:41:23 +00:00
Ralf Haferkamp 085f9181aa Merge pull request #2855 from opencloud-eu/dependabot/go_modules/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc-1.44.0
build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.44.0
2026-06-01 10:39:04 +02:00
Ralf Haferkamp 31e9aaa522 chore: bump reva to 2.46.1 2026-06-01 10:36:24 +02:00
oauth 36c496c230 🎉 Release 7.0.1 2026-06-01 07:33:58 +00:00
oauth b76f10950b Merge branch 'origin/main' into 'next-release/main' 2026-06-01 07:33:57 +00:00
Ralf Haferkamp f75a27e79c Merge pull request #2854 from opencloud-eu/dependabot/npm_and_yarn/services/idp/license-checker-rseidelsohn-5.0.1
build(deps-dev): bump license-checker-rseidelsohn from 4.4.2 to 5.0.1 in /services/idp
2026-06-01 09:31:36 +02:00
Ralf Haferkamp 8bf2100c01 Merge pull request #2853 from opencloud-eu/dependabot/npm_and_yarn/services/idp/cldr-8.0.0
build(deps-dev): bump cldr from 7.9.0 to 8.0.0 in /services/idp
2026-06-01 09:30:06 +02:00
dependabot[bot] 1f6cbaf39e build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) from 1.43.0 to 1.44.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 16:54:34 +00:00
dependabot[bot] fbc08a5ccf build(deps-dev): bump license-checker-rseidelsohn in /services/idp
Bumps [license-checker-rseidelsohn](https://github.com/RSeidelsohn/license-checker-rseidelsohn) from 4.4.2 to 5.0.1.
- [Release notes](https://github.com/RSeidelsohn/license-checker-rseidelsohn/releases)
- [Changelog](https://github.com/RSeidelsohn/license-checker-rseidelsohn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/RSeidelsohn/license-checker-rseidelsohn/compare/v4.4.2...v5.0.1)

---
updated-dependencies:
- dependency-name: license-checker-rseidelsohn
  dependency-version: 5.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 16:54:21 +00:00
dependabot[bot] c3dc7b370f build(deps-dev): bump cldr from 7.9.0 to 8.0.0 in /services/idp
Bumps [cldr](https://github.com/papandreou/node-cldr) from 7.9.0 to 8.0.0.
- [Changelog](https://github.com/papandreou/node-cldr/blob/master/CHANGELOG.md)
- [Commits](https://github.com/papandreou/node-cldr/compare/v7.9.0...v8.0.0)

---
updated-dependencies:
- dependency-name: cldr
  dependency-version: 8.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 16:54:04 +00:00
oauth 6bd81b7302 🎉 Release 7.0.1 2026-05-28 15:04:47 +00:00
oauth e381df016e Merge branch 'origin/main' into 'next-release/main' 2026-05-28 15:04:46 +00:00
Ralf Haferkamp 31340e30fb Merge pull request #2851 from rhafer/issue/2844
fix: Send SSE events for SpaceCreated/-Disabled/-Deleted
2026-05-28 17:02:03 +02:00
oauth c9fb429b1a 🎉 Release 7.0.1 2026-05-28 13:33:56 +00:00
oauth d561ab1ebc Merge branch 'origin/main' into 'next-release/main' 2026-05-28 13:33:56 +00:00
Ralf Haferkamp 11b6d7793d Merge pull request #2849 from opencloud-eu/dependabot/npm_and_yarn/services/idp/i18next-26.3.0
build(deps): bump i18next from 26.1.0 to 26.3.0 in /services/idp
2026-05-28 15:31:32 +02:00
oauth 9d36da169b 🎉 Release 7.0.1 2026-05-28 13:00:26 +00:00
oauth 6ca9a3e1c4 Merge branch 'origin/main' into 'next-release/main' 2026-05-28 13:00:26 +00:00
Ralf Haferkamp 3e875017a6 Merge pull request #2845 from opencloud-eu/dependabot/npm_and_yarn/services/idp/sass-loader-17.0.0
build(deps-dev): bump sass-loader from 16.0.8 to 17.0.0 in /services/idp
2026-05-28 14:58:00 +02:00
Ralf Haferkamp 69b9031b2d fix: Send SSE events for SpaceCreated/-Disabled/-Deleted
Closes: #2844
2026-05-28 14:34:17 +02:00
oauth 74bf60cbd9 🎉 Release 7.0.1 2026-05-28 08:50:54 +00:00
oauth d9c775d250 Merge branch 'origin/main' into 'next-release/main' 2026-05-28 08:50:53 +00:00
Ralf Haferkamp 5dba8ecc4e Merge pull request #2848 from opencloud-eu/dependabot/go_modules/google.golang.org/grpc-1.81.1
build(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1
2026-05-28 10:48:21 +02:00
dependabot[bot] 552e5dd289 build(deps-dev): bump sass-loader from 16.0.8 to 17.0.0 in /services/idp
Bumps [sass-loader](https://github.com/webpack/sass-loader) from 16.0.8 to 17.0.0.
- [Release notes](https://github.com/webpack/sass-loader/releases)
- [Changelog](https://github.com/webpack/sass-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/sass-loader/compare/v16.0.8...v17.0.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-version: 17.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 08:47:57 +00:00
dependabot[bot] 5ad4ea4413 build(deps): bump i18next from 26.1.0 to 26.3.0 in /services/idp
Bumps [i18next](https://github.com/i18next/i18next) from 26.1.0 to 26.3.0.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.1.0...v26.3.0)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 08:47:53 +00:00
Ralf Haferkamp a023172421 Merge pull request #2847 from opencloud-eu/dependabot/go_modules/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc-0.69.0
build(deps): bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.68.0 to 0.69.0
2026-05-28 10:47:40 +02:00
oauth aac062c9e2 🎉 Release 7.0.1 2026-05-28 08:47:24 +00:00
oauth d7f287c813 Merge branch 'origin/main' into 'next-release/main' 2026-05-28 08:47:24 +00:00
Ralf Haferkamp 52b05be436 Merge pull request #2846 from opencloud-eu/dependabot/npm_and_yarn/services/idp/typescript-6.0.3
build(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /services/idp
2026-05-28 10:45:06 +02:00
oauth a609940cb8 🎉 Release 7.0.1 2026-05-28 07:36:49 +00:00
oauth 3c9af15d54 Merge branch 'origin/main' into 'next-release/main' 2026-05-28 07:36:49 +00:00
dependabot[bot] a5ead45c87 build(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.80.0 to 1.81.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.80.0...v1.81.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.81.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 07:36:24 +00:00
dependabot[bot] 96478695cf build(deps): bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
Bumps [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.68.0 to 0.69.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-version: 0.69.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 07:36:14 +00:00
dependabot[bot] 44cef0ae65 build(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /services/idp
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 07:36:14 +00:00
Ralf Haferkamp 69989fea49 Merge pull request #2840 from rhafer/fix-dependabot
chore: fix dependabot config
2026-05-28 09:34:21 +02:00
Ralf Haferkamp b7cddc215d chore: fix dependabot config
It's `cooldown.default-days` not `cooldown.default`.
2026-05-27 16:59:31 +02:00
oauth f86da12a5e 🎉 Release 7.0.1 2026-05-27 14:10:25 +00:00
oauth 0ebbe33d3a Merge branch 'origin/main' into 'next-release/main' 2026-05-27 14:10:25 +00:00
Ralf Haferkamp 49726b8af8 Merge pull request #2834 from aduffeck/fix-space-search
Only try to limit search to spaces if there's a space id to limit to
2026-05-27 16:07:55 +02:00
oauth 6287c9e885 🎉 Release 7.0.1 2026-05-27 13:43:25 +00:00
oauth 217b49a3d1 Merge branch 'origin/main' into 'next-release/main' 2026-05-27 13:43:24 +00:00
Ralf Haferkamp 4cf1331dc9 Merge pull request #2839 from rhafer/issue/1022
fix(init): Only log admin password if it was generated
2026-05-27 15:40:57 +02:00
oauth 370a685482 🎉 Release 7.0.1 2026-05-27 13:34:12 +00:00
oauth 19333cb99a Merge branch 'origin/main' into 'next-release/main' 2026-05-27 13:34:12 +00:00
Ralf Haferkamp bf397732e7 docs(adr): Remove erroneous mention of kanidm
Apparently kanidm does allow to set the client-id.
2026-05-27 15:31:48 +02:00
Ralf Haferkamp da0b9417cd fix(init): Only log admin password if it was generated
This fixes `opencloud init` to only print the admin password if it was
generated by `init`. No longer log is when is was set via env var or command
line arguments.

Additionally add a `--quiet` command line flag to disable all non error
output.

Fixes: #1022
2026-05-27 13:22:29 +02:00
oauth 1162e26b16 🎉 Release 7.0.1 2026-05-27 10:15:34 +00:00
oauth a28998e536 Merge branch 'origin/main' into 'next-release/main' 2026-05-27 10:15:33 +00:00
Ralf Haferkamp 81572a0a24 Merge pull request #2838 from rhafer/bump-js
chore: bump js dependencies in idp
2026-05-27 12:13:17 +02:00
Ralf Haferkamp 5efe80577f Merge pull request #2836 from rhafer/issue/2833
fix: translations for activities and others
2026-05-27 12:12:50 +02:00
v.scharf c445f1fc93 adjust tets failures file 2026-05-27 11:56:52 +02:00
Ralf Haferkamp 70c0f4388b chore: bump js dependencies in idp 2026-05-27 11:46:34 +02:00
Ralf Haferkamp 719bb2123c fix: translations for activities and others
This fixes the server-side translations for "activties" and e.g. the
default space description. We need to bump "leonelquinteros/gotext"
to latest master for that as even the latest release still contains
and issue that cause `go vet` to complain about non-constant
format strings.

Fixes: #2833, #2835
2026-05-27 11:22:26 +02:00
Ralf Haferkamp c98cca36c6 Merge pull request #2823 from aduffeck/fix-build
Fix build by adding the missing protoc-gen-go-grpc dependency
2026-05-27 11:19:21 +02:00
oauth caa182cce3 🎉 Release 7.0.1 2026-05-27 08:24:49 +00:00
oauth 0c0b6d7403 Merge branch 'origin/main' into 'next-release/main' 2026-05-27 08:24:49 +00:00
Ralf Haferkamp bceaf4811f Merge pull request #2830 from opencloud-eu/dependabot/npm_and_yarn/services/idp/postcss-loader-8.2.1
build(deps-dev): bump postcss-loader from 4.3.0 to 8.2.1 in /services/idp
2026-05-27 10:22:41 +02:00
André Duffeck 5b9ec396c4 Dynamically adapt the URL prefix depending on the request 2026-05-27 10:04:01 +02:00
oauth b5e8c1a268 🎉 Release 7.0.1 2026-05-27 07:00:24 +00:00
oauth a69786e933 Merge branch 'origin/main' into 'next-release/main' 2026-05-27 07:00:23 +00:00
Ralf Haferkamp 613288e02a Merge pull request #2814 from opencloud-eu/dependabot/go_modules/github.com/riandyrn/otelchi-0.12.3
build(deps): bump github.com/riandyrn/otelchi from 0.12.2 to 0.12.3
2026-05-27 08:56:31 +02:00
v.scharf 071b09c504 remove tests from expected failures file 2026-05-26 15:15:04 +02:00
André Duffeck 6bb2324233 Only try to limit search to spaces if there's a space id to limit to 2026-05-26 13:42:57 +02:00
oauth e8d5249c93 🎉 Release 7.0.1 2026-05-26 11:39:51 +00:00
oauth ca347ad0e0 Merge branch 'origin/main' into 'next-release/main' 2026-05-26 11:39:51 +00:00
Viktor Scharf cff56490cb fix-2824. run tests without remote.php (#2826) 2026-05-26 13:35:49 +02:00
dependabot[bot] 68c0dceb49 build(deps-dev): bump postcss-loader in /services/idp
Bumps [postcss-loader](https://github.com/webpack/postcss-loader) from 4.3.0 to 8.2.1.
- [Release notes](https://github.com/webpack/postcss-loader/releases)
- [Changelog](https://github.com/webpack/postcss-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/postcss-loader/compare/v4.3.0...v8.2.1)

---
updated-dependencies:
- dependency-name: postcss-loader
  dependency-version: 8.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-25 20:54:43 +00:00
André DuffeckandItsGamerik 6c5e486924 Fix build by adding the missing protoc-gen-go-grpc dependency
Co-authored-by: ItsGamerik
2026-05-22 11:51:03 +02:00
Andre Duffeck 29f0387323 Merge pull request #2813 from aduffeck/parentid-consistency
Verify parent ids during the consistency check
2026-05-22 11:18:53 +02:00
André Duffeck c336e4ae34 Bump reva 2026-05-22 10:37:47 +02:00
André Duffeck fbc868522d Verify parent ids during the consistency check
Fixes #2372
2026-05-22 10:37:20 +02:00
oauth 882d20cc7f 🎉 Release 7.0.1 2026-05-22 08:33:11 +00:00
Andre Duffeck b189fadbff Merge pull request #2789 from aduffeck/posixfs-scan-command
Add a "posixfs scan" command for scanning the posix tree for new files
2026-05-22 10:29:40 +02:00
dependabot[bot] 97d541d956 build(deps): bump github.com/riandyrn/otelchi from 0.12.2 to 0.12.3
Bumps [github.com/riandyrn/otelchi](https://github.com/riandyrn/otelchi) from 0.12.2 to 0.12.3.
- [Release notes](https://github.com/riandyrn/otelchi/releases)
- [Changelog](https://github.com/riandyrn/otelchi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/riandyrn/otelchi/compare/v0.12.2...v0.12.3)

---
updated-dependencies:
- dependency-name: github.com/riandyrn/otelchi
  dependency-version: 0.12.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 16:18:29 +00:00
Ralf Haferkamp bdff962639 Merge pull request #2781 from opencloud-eu/dependabot/npm_and_yarn/services/idp/workbox-webpack-plugin-7.4.1
build(deps-dev): bump workbox-webpack-plugin from 7.4.0 to 7.4.1 in /services/idp
2026-05-21 15:52:10 +02:00
Ralf Haferkamp 3cfc1609ae Merge pull request #2810 from rhafer/bump-pnpm
chore(idp): bump to pnpm-11 and migrate config
2026-05-21 15:51:51 +02:00
Ralf Haferkamp 39d8979360 chore: dependabot changes
- allow it to open up to 4 PRs in parallel
- change the npm interval to daily
- set cooldown to 1 day to reflect the `minimumReleaseAge` default
  of pnpm
2026-05-21 14:08:02 +02:00
Ralf Haferkamp 075bddd1cb chore(idp): bump to pnpm-11 and migrate config 2026-05-21 12:32:00 +02:00
dependabot[bot] 17c27372fe build(deps-dev): bump workbox-webpack-plugin in /services/idp
Bumps [workbox-webpack-plugin](https://github.com/googlechrome/workbox) from 7.4.0 to 7.4.1.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v7.4.0...v7.4.1)

---
updated-dependencies:
- dependency-name: workbox-webpack-plugin
  dependency-version: 7.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 10:21:23 +00:00
André Duffeck 9de4e24395 Add a "posixfs scan" command for scanning the posix tree for new files 2026-05-21 11:18:24 +02:00
411 changed files with 37139 additions and 36943 deletions

No files matched your search

+3 -3
View File
@@ -83,11 +83,11 @@ $(PIGEON): $(BINGO_DIR)/pigeon.mod
@echo "(re)installing $(GOBIN)/pigeon-v1.3.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=pigeon.mod -o=$(GOBIN)/pigeon-v1.3.0 "github.com/mna/pigeon"
PROTOC_GEN_GO_GRPC := $(GOBIN)/protoc-gen-go-grpc-v1.81.0
PROTOC_GEN_GO_GRPC := $(GOBIN)/protoc-gen-go-grpc-v1.6.2
$(PROTOC_GEN_GO_GRPC): $(BINGO_DIR)/protoc-gen-go-grpc.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)/protoc-gen-go-grpc-v1.81.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-go-grpc.mod -o=$(GOBIN)/protoc-gen-go-grpc-v1.81.0 "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
@echo "(re)installing $(GOBIN)/protoc-gen-go-grpc-v1.6.2"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-go-grpc.mod -o=$(GOBIN)/protoc-gen-go-grpc-v1.6.2 "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
PROTOC_GEN_GO := $(GOBIN)/protoc-gen-go-v1.28.1
$(PROTOC_GEN_GO): $(BINGO_DIR)/protoc-gen-go.mod
+1 -1
View File
@@ -30,7 +30,7 @@ MUTAGEN="${GOBIN}/mutagen-v0.18.1"
PIGEON="${GOBIN}/pigeon-v1.3.0"
PROTOC_GEN_GO_GRPC="${GOBIN}/protoc-gen-go-grpc-v1.81.0"
PROTOC_GEN_GO_GRPC="${GOBIN}/protoc-gen-go-grpc-v1.6.2"
PROTOC_GEN_GO="${GOBIN}/protoc-gen-go-v1.28.1"
+5 -4
View File
@@ -4,11 +4,12 @@ updates:
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 2
open-pull-requests-limit: 4
- package-ecosystem: "npm"
directory: "/services/idp"
schedule:
interval: "weekly"
open-pull-requests-limit: 2
interval: "daily"
open-pull-requests-limit: 4
cooldown:
default-days: 1
+1
View File
@@ -11,6 +11,7 @@ protoc-deps: $(BINGO)
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/owncloud/protoc-gen-microweb
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/favadi/protoc-go-inject-tag
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l google.golang.org/grpc/cmd/protoc-gen-go-grpc
.PHONY: buf-generate
buf-generate: $(SHA1_LOCK_FILE)
+1 -1
View File
@@ -1,4 +1,4 @@
# The test runner source for UI tests
WEB_COMMITID=9de6541fc8bb116fcc78b09da697563faa5e61bd
WEB_COMMITID=2cee98bb27fab223fc4ce2455f5483ce7767ff1a
WEB_BRANCH=main
+14 -14
View File
@@ -109,10 +109,10 @@ config = {
},
"basic2": {
"suites": [
"apiDownloads",
"apiDepthInfinity",
"apiLocks",
"apiActivities",
"apiArchiver",
"apiContract",
"apiCors",
"apiAsyncUpload",
],
"skip": False,
},
@@ -121,7 +121,7 @@ config = {
"apiSettings",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
"emailNeeded": True,
"extraTestEnvironment": {
"EMAIL_HOST": "email",
@@ -144,14 +144,14 @@ config = {
#"collaborativePosix",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
},
"graphUserGroup": {
"suites": [
"apiGraphUserGroup",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
},
"spaces": {
"suites": [
@@ -209,7 +209,7 @@ config = {
"apiNotification",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
"emailNeeded": True,
"extraTestEnvironment": {
"EMAIL_HOST": "email",
@@ -251,7 +251,7 @@ config = {
"apiOcm",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
"federationServer": True,
"emailNeeded": True,
"extraTestEnvironment": {
@@ -287,14 +287,14 @@ config = {
"apiAuthApp",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
},
"cliCommands": {
"suites": [
"cliCommands",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
"antivirusNeeded": True,
"extraServerEnvironment": {
"ANTIVIRUS_SCANNER_TYPE": "clamav",
@@ -309,7 +309,7 @@ config = {
"apiTenancy",
],
"skip": False,
"withRemotePhp": [True],
"withRemotePhp": [False],
"ldapNeeded": True,
"extraTestEnvironment": {
"USE_PREPARED_LDAP_USERS": True,
@@ -1199,7 +1199,7 @@ def localApiTestPipeline(ctx):
"federationServer": False,
"collaborationServiceNeeded": False,
"extraCollaborationEnvironment": {},
"withRemotePhp": [True],
"withRemotePhp": [False],
"enableWatchFs": [False],
"ldapNeeded": False,
}
@@ -1316,7 +1316,7 @@ def localApiTest(suites, storage = "decomposed", extra_environment = {}, with_re
def coreApiTestPipeline(ctx):
defaults = {
"withRemotePhp": [True],
"withRemotePhp": [False],
"enableWatchFs": [False],
"storages": ["posix"],
"numberOfParts": 7,
+38
View File
@@ -1,5 +1,43 @@
# Changelog
## [7.1.0](https://github.com/opencloud-eu/opencloud/releases/tag/v7.1.0) - 2026-06-02
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @aduffeck, @dragonchaser, @kulmann, @micbar, @rhafer
### 🐛 Bug Fixes
- Prevent personal space creation for service- and lightweight users [[#2876](https://github.com/opencloud-eu/opencloud/pull/2876)]
- chore: bump reva to 2.46.1 [[#2869](https://github.com/opencloud-eu/opencloud/pull/2869)]
- fix: Send SSE events for SpaceCreated/-Disabled/-Deleted [[#2851](https://github.com/opencloud-eu/opencloud/pull/2851)]
- Only try to limit search to spaces if there's a space id to limit to [[#2834](https://github.com/opencloud-eu/opencloud/pull/2834)]
- fix(init): Only log admin password if it was generated [[#2839](https://github.com/opencloud-eu/opencloud/pull/2839)]
- fix: translations for activities and others [[#2836](https://github.com/opencloud-eu/opencloud/pull/2836)]
- fix-2824. run tests without remote.php [[#2826](https://github.com/opencloud-eu/opencloud/pull/2826)]
### 📈 Enhancement
- chore: bump web to v7.1.0 [[#2870](https://github.com/opencloud-eu/opencloud/pull/2870)]
### 📚 Documentation
- docs(adr): Remove erroneous mention of kanidm [[#2783](https://github.com/opencloud-eu/opencloud/pull/2783)]
### 📦️ Dependencies
- build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.44.0 [[#2855](https://github.com/opencloud-eu/opencloud/pull/2855)]
- build(deps-dev): bump license-checker-rseidelsohn from 4.4.2 to 5.0.1 in /services/idp [[#2854](https://github.com/opencloud-eu/opencloud/pull/2854)]
- build(deps-dev): bump cldr from 7.9.0 to 8.0.0 in /services/idp [[#2853](https://github.com/opencloud-eu/opencloud/pull/2853)]
- build(deps): bump i18next from 26.1.0 to 26.3.0 in /services/idp [[#2849](https://github.com/opencloud-eu/opencloud/pull/2849)]
- build(deps-dev): bump sass-loader from 16.0.8 to 17.0.0 in /services/idp [[#2845](https://github.com/opencloud-eu/opencloud/pull/2845)]
- build(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1 [[#2848](https://github.com/opencloud-eu/opencloud/pull/2848)]
- build(deps): bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.68.0 to 0.69.0 [[#2847](https://github.com/opencloud-eu/opencloud/pull/2847)]
- build(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /services/idp [[#2846](https://github.com/opencloud-eu/opencloud/pull/2846)]
- build(deps-dev): bump postcss-loader from 4.3.0 to 8.2.1 in /services/idp [[#2830](https://github.com/opencloud-eu/opencloud/pull/2830)]
- build(deps): bump github.com/riandyrn/otelchi from 0.12.2 to 0.12.3 [[#2814](https://github.com/opencloud-eu/opencloud/pull/2814)]
- build(deps-dev): bump workbox-webpack-plugin from 7.4.0 to 7.4.1 in /services/idp [[#2781](https://github.com/opencloud-eu/opencloud/pull/2781)]
## [7.0.0](https://github.com/opencloud-eu/opencloud/releases/tag/v7.0.0) - 2026-05-21
### ❤️ Thanks to all contributors! ❤️
@@ -18,7 +18,7 @@ OpenCloud with various existing identity providers. For example:
- Authentik basically creates a different issuer URL for each client. As OpenCloud
can only work with a single issuer URL, all OpenCloud clients need to use the
same client id to work with Authentik.
- Some IDPs (kanidm) are not able to work with user-supplied client ids. They generate
- Some IDPs are not able to work with user-supplied client ids. They generate
client ids automatically and do not allow to specify them manually.
- To make features like automatic role assignment work, clients need to request
specific scopes, depending on which exact IDP is used.
+22 -24
View File
@@ -33,8 +33,9 @@ require (
github.com/go-micro/plugins/v4/store/nats-js-kv v0.0.0-20240726082623-6831adfdcdc4
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-playground/validator/v10 v10.30.2
github.com/go-playground/validator/v10 v10.30.3
github.com/go-resty/resty/v2 v2.17.2
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
@@ -42,17 +43,16 @@ require (
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.7
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
github.com/invopop/validation v0.8.0
github.com/jellydator/ttlcache/v2 v2.11.1
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jinzhu/now v1.1.5
github.com/justinas/alice v1.2.0
github.com/kovidgoyal/imaging v1.8.20
github.com/leonelquinteros/gotext v1.7.2
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.66.0
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.14.0
@@ -64,14 +64,14 @@ require (
github.com/open-policy-agent/opa v1.15.2
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
github.com/opencloud-eu/reva/v2 v2.46.0
github.com/opencloud-eu/reva/v2 v2.46.2
github.com/opensearch-project/opensearch-go/v4 v4.6.0
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.12
github.com/prometheus/client_golang v1.23.2
github.com/r3labs/sse/v2 v2.10.0
github.com/riandyrn/otelchi v0.12.2
github.com/riandyrn/otelchi v0.12.3
github.com/rogpeppe/go-internal v1.14.1
github.com/rs/cors v1.11.1
github.com/rs/zerolog v1.35.0
@@ -94,24 +94,24 @@ require (
github.com/xhit/go-simple-mail/v2 v2.16.0
go-micro.dev/v4 v4.11.0
go.etcd.io/bbolt v1.4.3
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
go.opentelemetry.io/contrib/zpages v0.68.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/trace v1.43.0
golang.org/x/crypto v0.50.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/trace v1.44.0
golang.org/x/crypto v0.52.0
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/image v0.40.0
golang.org/x/net v0.53.0
golang.org/x/net v0.55.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
golang.org/x/term v0.42.0
golang.org/x/term v0.43.0
golang.org/x/text v0.37.0
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9
google.golang.org/grpc v1.80.0
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@@ -223,7 +223,6 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
@@ -315,7 +314,7 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/pablodz/inotifywaitgo v0.0.9 // indirect
github.com/pablodz/inotifywaitgo v0.0.12 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
@@ -382,19 +381,19 @@ require (
go.etcd.io/etcd/client/v3 v3.6.11 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
@@ -413,5 +412,4 @@ exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a
// to get the logger injection (https://github.com/pablodz/inotifywaitgo/pull/11)
replace github.com/pablodz/inotifywaitgo v0.0.9 => github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9
replace github.com/opencloud-eu/reva/v2 => github.com/opencloud-eu/reva/v2 v2.46.3-0.20260609111154-079ecc53abaa
+44 -46
View File
@@ -449,8 +449,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.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
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=
@@ -466,8 +466,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
@@ -613,8 +613,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vb
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -756,8 +756,8 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
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.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69 h1:ZLo0bXX17iUZpb/Sp7N+1uK4Jb7dl0fm5x5ETfzYSvY=
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69/go.mod h1:ksG5iXViKefoupjy+0qQjAVoaDnylnQ1ejWl9g14wh8=
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
@@ -857,8 +857,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mna/pigeon v1.3.0 h1:/3fzVrl1C2RK3x04tyL+ribn+3S3VSEFFbCFLmRPAoc=
@@ -948,12 +946,10 @@ github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-202505121527
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+lP5lUUIzjRGDg93WrQfZJZCaV1ZP3KeyXi8bzY=
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU=
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.46.0 h1:wYUHIHOsLP9vad/K19/52RRfoooeHUGY4BSeowNrbpY=
github.com/opencloud-eu/reva/v2 v2.46.0/go.mod h1:fWAzVpZlJQEY/qeIrd9d3U+LpqY9JGsjJ2dc0a1jCEs=
github.com/opencloud-eu/reva/v2 v2.46.3-0.20260609111154-079ecc53abaa h1:MSS7EwbPh74IpH0Tidn6JoeMNF1D1A66MaWk0Ugqnv0=
github.com/opencloud-eu/reva/v2 v2.46.3-0.20260609111154-079ecc53abaa/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -972,6 +968,8 @@ github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CF
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pablodz/inotifywaitgo v0.0.12 h1:DVJJTFMDlSAdO46yisPF2vzozs/r+4k6ih8MVghq78M=
github.com/pablodz/inotifywaitgo v0.0.12/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
@@ -1065,8 +1063,8 @@ github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKc
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/riandyrn/otelchi v0.12.2 h1:6QhGv0LVw/dwjtPd12mnNrl0oEQF4ZAlmHcnlTYbeAg=
github.com/riandyrn/otelchi v0.12.2/go.mod h1:weZZeUJURvtCcbWsdb7Y6F8KFZGedJlSrgUjq9VirV8=
github.com/riandyrn/otelchi v0.12.3 h1:KW9gA+97d6mExk8vbh0FRwb2biUvpyYlc8YuxP1Oap0=
github.com/riandyrn/otelchi v0.12.3/go.mod h1:weZZeUJURvtCcbWsdb7Y6F8KFZGedJlSrgUjq9VirV8=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@@ -1300,28 +1298,28 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/contrib/zpages v0.68.0 h1:H5yrUwxPrbvhzdBxjQD+VXMtPjIBfp8NWNVvQT8E30M=
go.opentelemetry.io/contrib/zpages v0.68.0/go.mod h1:sZGctYYO4UOHItj9bx3F+t/s+u1Fv8CHCJ5s2eR2cjU=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1362,8 +1360,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1453,8 +1451,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
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=
@@ -1560,8 +1558,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
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=
@@ -1570,8 +1568,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
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=
@@ -1711,10 +1709,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1730,8 +1728,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
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=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+3 -1
View File
@@ -35,9 +35,10 @@ func InitCommand(_ *config.Config) *cobra.Command {
}
forceOverwriteFlag := viper.GetBool("force-overwrite")
diffFlag, _ := cmd.Flags().GetBool("diff")
quietFlag, _ := cmd.Flags().GetBool("quiet")
configPathFlag := viper.GetString("config-path")
adminPasswordFlag := viper.GetString("admin-password")
err := ocinit.CreateConfig(insecure, forceOverwriteFlag, diffFlag, configPathFlag, adminPasswordFlag)
err := ocinit.CreateConfig(insecure, forceOverwriteFlag, diffFlag, configPathFlag, adminPasswordFlag, quietFlag)
if err != nil {
log.Fatalf("Could not create config: %s", err)
}
@@ -49,6 +50,7 @@ func InitCommand(_ *config.Config) *cobra.Command {
_ = viper.BindPFlag("insecure", initCmd.Flags().Lookup("insecure"))
initCmd.Flags().BoolP("diff", "d", false, "Show the difference between the current config and the new one")
initCmd.Flags().BoolP("quiet", "q", false, "Work quietly. Surpresses and non-error message")
initCmd.Flags().BoolP("force-overwrite", "f", false, "Force overwrite existing config file")
_ = viper.BindEnv("force-overwrite", "OC_FORCE_CONFIG_OVERWRITE")
+156 -12
View File
@@ -10,8 +10,18 @@ import (
"github.com/opencloud-eu/opencloud/opencloud/pkg/register"
"github.com/opencloud-eu/opencloud/pkg/config"
"github.com/opencloud-eu/opencloud/pkg/config/configlog"
"github.com/opencloud-eu/opencloud/pkg/config/parser"
storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser"
"github.com/opencloud-eu/opencloud/services/storage-users/pkg/event"
"github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/registry"
"github.com/pkg/xattr"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
"github.com/theckman/yacspin"
"github.com/vmihailenco/msgpack/v5"
@@ -28,8 +38,13 @@ const (
var (
spinner *yacspin.Spinner
restartRequired = false
ignorer *ignore.Ignorer
)
type IDCacher interface {
WarmupIDCache(root string, assimilate, onlyDirty bool) error
}
// EntryInfo holds information about a directory entry.
type EntryInfo struct {
Path string
@@ -46,6 +61,7 @@ func PosixfsCommand(cfg *config.Config) *cobra.Command {
}
posixCmd.AddCommand(consistencyCmd(cfg))
posixCmd.AddCommand(scanCmd(cfg))
return posixCmd
}
@@ -54,6 +70,73 @@ func init() {
register.AddCommand(PosixfsCommand)
}
// scanCmd performs a posixfs id cache warmup scan
func scanCmd(ocCfg *config.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "scan",
Short: "Perform a filesystem scan and update the ID and filemetadata cache",
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := parser.ParseConfig(ocCfg, true); err != nil {
return configlog.ReturnError(err)
}
// Parse storage users config
ocCfg.StorageUsers.Commons = ocCfg.Commons
return configlog.ReturnFatal(storageUsersParser.ParseConfig(ocCfg.StorageUsers))
},
RunE: func(cmd *cobra.Command, args []string) error {
cfg := ocCfg.StorageUsers
if cfg.Driver != "posix" {
fmt.Fprintf(os.Stderr, "This command is only available when using the 'posix' driver. Current driver: '%s'\n", cfg.Driver)
os.Exit(1)
}
// We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards
drivers := revaconfig.StorageProviderDrivers(cfg)
drivers["posix"] = revaconfig.Posix(cfg, false, false)
var fsStream events.Stream
var err error
fsStream, err = event.NewStream(cfg)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create event stream for posix driver: %v\n", err)
os.Exit(1)
}
f, ok := registry.NewFuncs["posix"]
if !ok {
fmt.Fprintf(os.Stderr, "posix driver not found in registry\n")
os.Exit(1)
}
fs, err := f(drivers["posix"].(map[string]any), fsStream, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize filesystem driver '%s': %v\n", cfg.Driver, err)
return err
}
cacher, ok := fs.(IDCacher)
if !ok {
fmt.Fprintf(os.Stderr, "The posix driver does not expose WarmupIDCache.\n")
os.Exit(1)
}
fmt.Println("Starting posixfs scan...")
err = cacher.WarmupIDCache(cfg.Drivers.Posix.Root, true, false)
if err != nil {
fmt.Fprintf(os.Stderr, "Scan failed: %v\n", err)
return err
}
fmt.Println("Scan completed successfully.")
return nil
},
}
return cmd
}
// consistencyCmd returns a command to check the consistency of the posixfs storage.
func consistencyCmd(cfg *config.Config) *cobra.Command {
consCmd := &cobra.Command{
@@ -74,6 +157,12 @@ func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error {
rootPath, _ := cmd.Flags().GetString("root")
indexesPath := filepath.Join(rootPath, "indexes")
opt, _ := options.New(map[string]interface{}{
"root": rootPath,
})
log := zerolog.Nop()
ignorer = ignore.NewIgnorer(opt, &log)
_, err := os.Stat(indexesPath)
if err != nil {
if os.IsNotExist(err) {
@@ -130,6 +219,7 @@ func checkSpaces(basePath string) {
}
func checkSpace(spacePath string) {
spinner.Message("")
spinner.Suffix(fmt.Sprintf(" Checking space '%s'", spacePath))
info, err := os.Stat(spacePath)
@@ -149,10 +239,11 @@ func checkSpace(spacePath string) {
}
checkSpaceID(spacePath)
checkNodeIDs(spacePath)
}
func checkSpaceID(spacePath string) {
spinner.Message("checking space ID uniqueness")
spinner.Message(" - checking space ID uniqueness")
entries, uniqueIDs, oldestEntry, err := gatherAttributes(spacePath)
if err != nil {
@@ -161,7 +252,6 @@ func checkSpaceID(spacePath string) {
}
if len(entries) == 0 {
logSuccess("(empty space)")
return
}
@@ -199,8 +289,65 @@ func checkSpaceID(spacePath string) {
}
fixSpaceID(spacePath, obsoleteIDs, targetID, entries)
spinner.Unpause()
} else {
logSuccess("")
}
}
func walkParentIDs(dir string, parentID string) int {
fixes := 0
entries, err := os.ReadDir(dir)
if err != nil {
logFailure("Error reading directory '%s': %v", dir, err)
return 0
}
for _, entry := range entries {
fullPath := filepath.Join(dir, entry.Name())
if ignorer.IsIgnored(fullPath) {
continue
}
actualParentID, err := xattr.Get(fullPath, parentIDAttrName)
if err != nil || string(actualParentID) != parentID {
err = xattr.Set(fullPath, parentIDAttrName, []byte(parentID))
if err != nil {
logFailure("Failed to fix parent ID for '%s': %v", fullPath, err)
} else {
spinner.Pause()
fmt.Printf("\n + Fixed parent ID for '%s'\n", fullPath)
spinner.Unpause()
fixes++
restartRequired = true
}
}
if entry.IsDir() {
nodeID, err := xattr.Get(fullPath, idAttrName)
if err != nil || len(nodeID) == 0 {
logFailure("Directory '%s' missing '%s', skipping its children", fullPath, idAttrName)
continue
}
walkParentIDs(fullPath, string(nodeID))
}
}
return fixes
}
func checkNodeIDs(spacePath string) {
spinner.Message(" - checking parent IDs")
rootID, err := xattr.Get(spacePath, idAttrName)
if err != nil || len(rootID) == 0 {
logFailure("Space root '%s' missing '%s' attribute", spacePath, idAttrName)
return
}
fixes := walkParentIDs(spacePath, string(rootID))
if fixes > 0 {
spinner.Pause()
fmt.Printf("\n ✓ Fixed %d incorrect parent IDs in %s\n", fixes, filepath.Base(spacePath))
spinner.Unpause()
}
}
@@ -245,6 +392,9 @@ func gatherAttributes(path string) ([]EntryInfo, map[string]struct{}, EntryInfo,
for _, entry := range dirEntries {
fullPath := filepath.Join(path, entry.Name())
if ignorer.IsIgnored(fullPath) {
continue
}
info, err := os.Stat(fullPath)
if err != nil {
fmt.Printf(" - Warning: could not stat %s: %v\n", entry.Name(), err)
@@ -349,7 +499,7 @@ func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error {
return fmt.Errorf("failed to write updated index file: %w", err)
}
logSuccess("Successfully removed %d item(s) and saved index file.\n", itemsRemoved)
fmt.Printf("Successfully removed %d item(s) and saved index file.\n", itemsRemoved)
return nil
}
@@ -368,13 +518,7 @@ func removeAttributes(path string) error {
}
func logFailure(message string, args ...any) {
spinner.StopFailMessage(fmt.Sprintf(message, args...))
spinner.StopFailMessage(fmt.Sprintf("\n"+message, args...))
spinner.StopFail()
spinner.Start()
}
func logSuccess(message string, args ...any) {
spinner.StopMessage(fmt.Sprintf(message, args...))
spinner.Stop()
spinner.Start()
}
+16 -7
View File
@@ -47,15 +47,22 @@ func backupOpenCloudConfigFile(configPath string) (string, error) {
}
// printBanner prints the generated opencloud config banner.
func printBanner(targetPath, ocAdminServicePassword, targetBackupConfig string) {
func printBanner(targetPath, ocAdminServicePassword string, adminPWgenerated bool, targetBackupConfig string) {
fmt.Printf(
"\n=========================================\n"+
" generated OpenCloud Config\n"+
"=========================================\n"+
" configpath : %s\n"+
" user : admin\n"+
" password : %s\n\n",
targetPath, ocAdminServicePassword)
" configpath : %s\n",
targetPath,
)
if adminPWgenerated {
fmt.Printf(" user : admin\n"+
" password : %s\n",
ocAdminServicePassword,
)
}
fmt.Println()
if targetBackupConfig != "" {
fmt.Printf("\n=========================================\n"+
"An older config file has been backed up to\n %s\n\n",
@@ -64,13 +71,15 @@ func printBanner(targetPath, ocAdminServicePassword, targetBackupConfig string)
}
// writeConfig writes the config to the target path and prints a banner
func writeConfig(configPath, ocAdminServicePassword, targetBackupConfig string, yamlOutput []byte) error {
func writeConfig(configPath, ocAdminServicePassword, targetBackupConfig string, yamlOutput []byte, adminPWgenerated, quiet bool) error {
targetPath := path.Join(configPath, configFilename)
err := os.WriteFile(targetPath, yamlOutput, 0600)
if err != nil {
return err
}
printBanner(targetPath, ocAdminServicePassword, targetBackupConfig)
if !quiet {
printBanner(targetPath, ocAdminServicePassword, adminPWgenerated, targetBackupConfig)
}
return nil
}
+4 -2
View File
@@ -22,7 +22,7 @@ var (
)
// CreateConfig creates a config file with random passwords at configPath
func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string) error {
func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string, quiet bool) error {
if diff && forceOverwrite {
return fmt.Errorf("diff and force-overwrite flags are mutually exclusive")
}
@@ -69,6 +69,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
idmServicePassword, idpServicePassword, ocAdminServicePassword, revaServicePassword string
tokenManagerJwtSecret, collaborationWOPISecret, machineAuthAPIKey, systemUserAPIKey string
revaTransferSecret, thumbnailsTransferSecret, serviceAccountSecret, urlSigningSecret string
adminPasswdwordGenerated bool
)
if diff {
@@ -123,6 +124,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
if err != nil {
return fmt.Errorf("could not generate random password for opencloud admin: %s", err)
}
adminPasswdwordGenerated = true
}
revaServicePassword, err = generators.GenerateRandomPassword(passwordLength)
@@ -311,5 +313,5 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
if diff {
return writePatch(configPath, yamlOutput)
}
return writeConfig(configPath, ocAdminServicePassword, targetBackupConfig, yamlOutput)
return writeConfig(configPath, ocAdminServicePassword, targetBackupConfig, yamlOutput, adminPasswdwordGenerated, quiet)
}
+1 -1
View File
@@ -54,7 +54,7 @@ func NewTranslatorFromCommonConfig(defaultLocale string, domain string, path str
// Translate translates a string to the locale
func (t Translator) Translate(str, locale string) string {
return t.Locale(locale).Get("%s", str)
return t.Locale(locale).Get(str)
}
// Locale returns the gotext.Locale, use `.Get` method to translate strings
+1 -1
View File
@@ -34,7 +34,7 @@ var (
// LatestTag is the latest released version plus the dev meta version.
// Will be overwritten by the release pipeline
// Needs a manual change for every tagged release
LatestTag = "7.0.0+dev"
LatestTag = "7.1.0+dev"
// Date indicates the build date.
// This has been removed, it looks like you can only replace static strings with recent go versions
@@ -1,7 +1,7 @@
package revaconfig
import (
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/app-registry/pkg/config"
)
@@ -1,9 +1,6 @@
package defaults
import (
"path/filepath"
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/pkg/structs"
"github.com/opencloud-eu/opencloud/services/auth-basic/pkg/config"
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
AuthProvider: "ldap",
AuthProviders: config.AuthProviders{
LDAP: config.LDAPProvider{
URI: "ldaps://localhost:9235",
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
URI: "ldap://localhost:9236",
Insecure: false,
UserBaseDN: "ou=users,o=libregraph-idm",
GroupBaseDN: "ou=groups,o=libregraph-idm",
+3
View File
@@ -34,6 +34,9 @@ var _registeredEvents = []events.Unmarshaller{
events.FileLocked{},
events.FileUnlocked{},
events.FileTouched{},
events.SpaceCreated{},
events.SpaceDeleted{},
events.SpaceDisabled{},
events.SpaceShared{},
events.SpaceShareUpdated{},
events.SpaceUnshared{},
+23
View File
@@ -131,6 +131,11 @@ func (cl *ClientlogService) processEvent(event events.Event) {
users, data, err = processShareEvent(ctx, ref, gwc, event.InitiatorID, uid, gid)
}
spaceEv := func(typ string, id *provider.StorageSpaceId, uids []string) {
evType = typ
users, data, err = processSpaceEvent(ctx, id, gwc, event.InitiatorID, uids)
}
switch e := event.Event.(type) {
default:
err = errors.New("unhandled event")
@@ -160,6 +165,16 @@ func (cl *ClientlogService) processEvent(event events.Event) {
fileEv("file-unlocked", e.Ref)
case events.FileTouched:
fileEv("file-touched", e.Ref)
case events.SpaceCreated:
spaceEv("space-created", e.ID, []string{e.Executant.GetOpaqueId()})
case events.SpaceDisabled:
spaceEv("space-disabled", e.ID, []string{e.Executant.GetOpaqueId()})
case events.SpaceDeleted:
uids := []string{}
for k := range e.FinalMembers {
uids = append(uids, k)
}
spaceEv("space-deleted", e.ID, uids)
case events.SpaceShared:
r, _ := storagespace.ParseReference(e.ID.GetOpaqueId())
shareEv("space-member-added", &r, e.GranteeUserID, e.GranteeGroupID)
@@ -239,6 +254,14 @@ func processShareEvent(ctx context.Context, ref *provider.Reference, gwc gateway
return addShareeData(ctx, gwc, data, users, shareeID, shareeGroupID)
}
func processSpaceEvent(ctx context.Context, id *provider.StorageSpaceId, gwc gateway.GatewayAPIClient, initiatorid string, uids []string) ([]string, FileEvent, error) {
data := FileEvent{
SpaceID: id.GetOpaqueId(),
InitiatorID: initiatorid,
}
return uids, data, nil
}
// custom logic for item trashed event
func processItemTrashedEvent(ctx context.Context, ref *provider.Reference, gwc gateway.GatewayAPIClient, initiatorid string, itemID *provider.ResourceId) ([]string, FileEvent, error) {
data := FileEvent{
@@ -1,11 +1,9 @@
package defaults
import (
"path"
"strings"
"time"
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/pkg/structs"
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
@@ -79,9 +77,8 @@ func DefaultConfig() *config.Config {
Identity: config.Identity{
Backend: "ldap",
LDAP: config.LDAP{
URI: "ldaps://localhost:9235",
URI: "ldap://localhost:9236",
Insecure: false,
CACert: path.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
BindDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
UseServerUUID: false,
UsePasswordModExOp: true,
@@ -1,9 +1,6 @@
package defaults
import (
"path/filepath"
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/pkg/structs"
"github.com/opencloud-eu/opencloud/services/groups/pkg/config"
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
Driver: "ldap",
Drivers: config.Drivers{
LDAP: config.LDAPDriver{
URI: "ldaps://localhost:9235",
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
URI: "ldap://localhost:9236",
Insecure: false,
UserBaseDN: "ou=users,o=libregraph-idm",
GroupBaseDN: "ou=groups,o=libregraph-idm",
+15 -12
View File
@@ -8,10 +8,11 @@ import (
"html/template"
"os"
"os/signal"
"path"
"strings"
"github.com/opencloud-eu/opencloud/pkg/config/configlog"
pkgcrypto "github.com/opencloud-eu/opencloud/pkg/crypto"
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/runner"
"github.com/opencloud-eu/opencloud/services/idm"
@@ -47,23 +48,25 @@ func Server(cfg *config.Config) *cobra.Command {
gr := runner.NewGroup()
{
servercfg := server.Config{
Logger: log.LogrusWrap(logger.Logger),
LDAPHandler: "boltdb",
LDAPSListenAddr: cfg.IDM.LDAPSAddr,
TLSCertFile: cfg.IDM.Cert,
TLSKeyFile: cfg.IDM.Key,
LDAPBaseDN: "o=libregraph-idm",
LDAPAdminDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
Logger: log.LogrusWrap(logger.Logger),
LDAPHandler: "boltdb",
LDAPListenAddr: cfg.IDM.LDAPAddr,
LDAPBaseDN: "o=libregraph-idm",
LDAPAdminDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
BoltDBFile: cfg.IDM.DatabasePath,
}
if cfg.IDM.LDAPSAddr != "" {
// Generate a self-signing cert if no certificate is present
if err := pkgcrypto.GenCert(cfg.IDM.Cert, cfg.IDM.Key, logger); err != nil {
logger.Fatal().Err(err).Msgf("Could not generate test-certificate")
}
servercfg.LDAPSListenAddr = cfg.IDM.LDAPSAddr
servercfg.TLSCertFile = cfg.IDM.Cert
servercfg.TLSKeyFile = cfg.IDM.Key
}
if err := os.MkdirAll(path.Join(defaults.BaseDataPath(), "idm"), 0700); err != nil {
logger.Fatal().Err(err).Msgf("Could not create data directory for idm")
}
if _, err := os.Stat(servercfg.BoltDBFile); errors.Is(err, os.ErrNotExist) {
logger.Debug().Msg("Bootstrapping IDM database")
if err = bootstrap(logger, cfg, servercfg); err != nil {
+1
View File
@@ -27,6 +27,7 @@ type Config struct {
type Settings struct {
LDAPSAddr string `yaml:"ldaps_addr" env:"IDM_LDAPS_ADDR" desc:"Listen address for the LDAPS listener (ip-addr:port)." introductionVersion:"1.0.0"`
LDAPAddr string
Cert string `yaml:"cert" env:"IDM_LDAPS_CERT" desc:"File name of the TLS server certificate for the LDAPS listener. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
Key string `yaml:"key" env:"IDM_LDAPS_KEY" desc:"File name for the TLS certificate key for the server certificate. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
DatabasePath string `yaml:"database" env:"IDM_DATABASE_PATH" desc:"Full path to the IDM backend database. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
@@ -30,9 +30,7 @@ func DefaultConfig() *config.Config {
CreateDemoUsers: false,
DemoUsersIssuerUrl: "https://localhost:9200",
IDM: config.Settings{
LDAPSAddr: "127.0.0.1:9235",
Cert: path.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
Key: path.Join(defaults.BaseDataPath(), "idm", "ldap.key"),
LDAPAddr: "127.0.0.1:9236",
DatabasePath: path.Join(defaults.BaseDataPath(), "idm", "idm.boltdb"),
},
}
@@ -51,5 +49,5 @@ func EnsureDefaults(cfg *config.Config) {
// Sanitize sanitizes the configuration
func Sanitize(cfg *config.Config) {
// nothing to sanitize here
// nothing to do yet
}
+21
View File
@@ -2,6 +2,7 @@ package parser
import (
"errors"
"net"
occfg "github.com/opencloud-eu/opencloud/pkg/config"
"github.com/opencloud-eu/opencloud/pkg/shared"
@@ -53,5 +54,25 @@ func Validate(cfg *config.Config) error {
return shared.MissingServiceUserPassword(cfg.Service.Name, "REVA")
}
ip, err := net.ResolveTCPAddr("tcp", cfg.IDM.LDAPAddr) // validate the LDAP address if set
if err != nil {
return errors.New("invalid configuration: 'ldap_addr' is not a valid address")
}
if !ip.IP.IsLoopback() {
// loopback addresses are allowed to be used with ldap_addr, but not with ldaps_addr, for security reasons
return errors.New("invalid configuration: 'ldap_addr' is set but 'ldaps_addr' is not set. For security reasons, the 'ldap_addr' setting is only allowed to be used with loopback addresses. Please set 'ldaps_addr' to a valid address and port to listen for LDAPS connections")
}
if cfg.IDM.LDAPSAddr != "" {
if cfg.IDM.Cert == "" {
return errors.New("invalid configuration: 'ldaps_addr' is set but 'cert' is not set. Please set 'cert' to a valid path to a TLS certificate")
}
if cfg.IDM.Key == "" {
return errors.New("invalid configuration: 'ldaps_addr' is set but 'key' is not set. Please set 'key' to a valid path to a TLS certificate key")
}
}
return nil
}
-2
View File
@@ -1,2 +0,0 @@
auto-install-peers=true
public-hoist-pattern[]=*
+9 -16
View File
@@ -35,8 +35,8 @@
"@types/react-dom": "^17.0.26",
"@types/react-redux": "^7.1.34",
"@types/redux-logger": "^3.0.13",
"axios": "^1.16.0",
"i18next": "^26.1.0",
"axios": "^1.16.1",
"i18next": "^26.3.0",
"i18next-browser-languagedetector": "^8.2.1",
"i18next-resources-to-backend": "^1.2.1",
"query-string": "^9.3.1",
@@ -56,7 +56,7 @@
"babel-plugin-named-asset-import": "^0.3.8",
"babel-preset-react-app": "^10.1.0",
"case-sensitive-paths-webpack-plugin": "2.4.0",
"cldr": "^7.9.0",
"cldr": "^8.0.0",
"css-loader": "^7.1.4",
"css-minimizer-webpack-plugin": "^8.0.0",
"dotenv": "17.4.2",
@@ -64,30 +64,23 @@
"html-webpack-plugin": "^5.6.7",
"i18next-conv": "^15.1.2",
"i18next-parser": "^9.4.0",
"license-checker-rseidelsohn": "4.4.2",
"license-checker-rseidelsohn": "5.0.1",
"mini-css-extract-plugin": "2.9.2",
"pnp-webpack-plugin": "1.7.0",
"postcss-flexbugs-fixes": "5.0.2",
"postcss-loader": "4.3.0",
"postcss-loader": "8.2.1",
"postcss-normalize": "13.0.1",
"postcss-preset-env": "11.2.0",
"react-dev-utils": "^12.0.1",
"resolve": "^1.22.12",
"resolve-url-loader": "^5.0.0",
"sass-loader": "^16.0.8",
"sass-loader": "^17.0.0",
"source-map-explorer": "^2.5.3",
"typescript": "^5.9.3",
"typescript": "^6.0.3",
"url-loader": "4.1.1",
"webpack": "5.105.2",
"webpack-manifest-plugin": "5.0.0",
"workbox-webpack-plugin": "7.4.0"
"workbox-webpack-plugin": "7.4.1"
},
"pnpm": {
"overrides": {
"fast-uri": ">=3.1.2",
"serialize-javascript@<7.0.3": ">=7.0.3",
"@xmldom/xmldom": ">=0.8.13"
}
},
"packageManager": "pnpm@9.15.4"
"packageManager": "pnpm@11.1.3"
}
@@ -119,8 +119,7 @@ func DefaultConfig() *config.Config {
},
},
Ldap: config.Ldap{
URI: "ldaps://localhost:9235",
TLSCACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
URI: "ldap://localhost:9236",
BindDN: "uid=idp,ou=sysusers,o=libregraph-idm",
BaseDN: "ou=users,o=libregraph-idm",
Scope: "sub",
+1954 -1416
View File
File diff suppressed because it is too large. Load diff
+30
View File
@@ -0,0 +1,30 @@
autoInstallPeers: true
publicHoistPattern:
- "*"
# Ensure platform-specific optional dependencies (e.g. Rollup's native
# binaries pulled in via workbox) are fetched for every architecture we
# build/package for, not just the host that runs `pnpm install`. Without
# this, cross-arch RPM/OBS builds fail with "Cannot find module
# @rollup/rollup-linux-arm64-gnu" on aarch64.
supportedArchitectures:
os:
- current
- linux
cpu:
- x64
- arm64
libc:
- glibc
- musl
overrides:
fast-uri: ">=3.1.2"
postcss: ">=8.5.10"
"serialize-javascript@<7.0.3": ">=7.0.3"
"@babel/plugin-transform-modules-systemjs": ">=7.29.4"
"@xmldom/xmldom": ">=0.8.13"
allowBuilds:
esbuild: true
@@ -1,9 +1,6 @@
package defaults
import (
"path/filepath"
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/pkg/structs"
"github.com/opencloud-eu/opencloud/services/users/pkg/config"
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
Driver: "ldap",
Drivers: config.Drivers{
LDAP: config.LDAPDriver{
URI: "ldaps://localhost:9235",
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
URI: "ldap://localhost:9236",
Insecure: false,
UserBaseDN: "ou=users,o=libregraph-idm",
GroupBaseDN: "ou=groups,o=libregraph-idm",
+1 -1
View File
@@ -1,6 +1,6 @@
SHELL := bash
NAME := web
WEB_ASSETS_VERSION = v7.0.1
WEB_ASSETS_VERSION = v7.1.0
WEB_ASSETS_BRANCH = main
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
+17 -10
View File
@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"path"
"regexp"
"slices"
"strconv"
"strings"
@@ -36,6 +37,8 @@ const (
// TODO elementNameFilterFiles = "filter-files"
)
var spacesSearchRegex = regexp.MustCompile(`^/(?:remote\.php/)?dav/spaces/([^/]+)`)
// Search is the endpoint for retrieving search results for REPORT requests
func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
logger := g.log.SubloggerWithRequestID(r.Context())
@@ -76,9 +79,9 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
PageSize: int32(rep.SearchFiles.Search.Limit),
}
// Limit search to the according space when searching /dav/spaces/
if strings.HasPrefix(r.URL.Path, "/dav/spaces") {
space := strings.TrimPrefix(r.URL.Path, "/dav/spaces/")
// Limit search to the according space when searching /dav/spaces/<spaceid>
if matches := spacesSearchRegex.FindStringSubmatch(r.URL.Path); matches != nil {
space := matches[1]
rid, err := storagespace.ParseID(space)
if err != nil {
logger.Debug().Err(err).Msg("error parsing the space id for filtering")
@@ -92,6 +95,10 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
}
}
}
davPrefix := ""
if strings.HasPrefix(r.URL.Path, "/remote.php") {
davPrefix = "/remote.php"
}
rsp, err := g.searchClient.Search(ctx, req)
if err != nil {
@@ -105,12 +112,12 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
logger.Error().Err(err).Msg("could not get search results")
return
}
g.sendSearchResponse(rsp, w, r, user)
g.sendSearchResponse(davPrefix, rsp, w, r, user)
}
func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
func (g Webdav) sendSearchResponse(davPrefix string, rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
logger := g.log.SubloggerWithRequestID(r.Context())
responsesXML, err := multistatusResponse(r.Context(), g.config.OpenCloudPublicURL, rsp.Matches, user)
responsesXML, err := multistatusResponse(r.Context(), davPrefix, g.config.OpenCloudPublicURL, rsp.Matches, user)
if err != nil {
logger.Error().Err(err).Msg("error formatting propfind")
w.WriteHeader(http.StatusInternalServerError)
@@ -128,10 +135,10 @@ func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.Respons
}
// multistatusResponse converts a list of matches into a multistatus response string
func multistatusResponse(ctx context.Context, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
func multistatusResponse(ctx context.Context, davPrefix, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
responses := make([]*propfind.ResponseXML, 0, len(matches))
for i := range matches {
res, err := matchToPropResponse(ctx, publicURL, matches[i], user)
res, err := matchToPropResponse(ctx, davPrefix, publicURL, matches[i], user)
if err != nil {
return nil, err
}
@@ -147,7 +154,7 @@ func multistatusResponse(ctx context.Context, publicURL string, matches []*searc
return msg, nil
}
func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
func matchToPropResponse(ctx context.Context, davPrefix, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
// unfortunately, search uses own versions of ResourceId and Ref. So we need to assert them here
var (
ref string
@@ -180,7 +187,7 @@ func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg
return nil, err
}
response := propfind.ResponseXML{
Href: net.EncodePath(path.Join("/remote.php/dav/spaces/", ref)),
Href: net.EncodePath(path.Join(davPrefix, "/dav/spaces/", ref)),
Propstat: []propfind.PropstatXML{},
}
@@ -0,0 +1,35 @@
package svc
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestSearch(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Search Suite")
}
var _ = Describe("SpacesSearchRegex", func() {
DescribeTable("path matching",
func(path, expectedSpace string, shouldMatch bool) {
matches := spacesSearchRegex.FindStringSubmatch(path)
if shouldMatch {
Expect(matches).ToNot(BeNil(), "Expected path %q to match", path)
Expect(matches[1]).To(Equal(expectedSpace), "Expected space to be %q", expectedSpace)
} else {
Expect(matches).To(BeNil(), "Expected path %q not to match", path)
}
},
Entry("standard dav spaces path", "/dav/spaces/12345", "12345", true),
Entry("remote.php dav spaces path", "/remote.php/dav/spaces/12345", "12345", true),
Entry("standard dav spaces path with subpaths", "/dav/spaces/12345/some/folder", "12345", true),
Entry("remote.php dav spaces path with subpaths", "/remote.php/dav/spaces/12345/some/folder", "12345", true),
Entry("standard dav spaces path without space", "/dav/spaces/", "", false),
Entry("remote.php dav spaces path without space", "/remote.php/dav/spaces/", "", false),
Entry("prefix match only", "/dav/spaces", "", false),
Entry("unrelated path", "/dav/files/123", "", false),
)
})
@@ -190,10 +190,6 @@
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
@@ -190,10 +190,6 @@
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
@@ -1,298 +1,9 @@
## Scenarios that are expected to fail when remote.php is not used
#### [REPORT request without remote.php returns empty result (only with dav/spaces path)](https://github.com/owncloud/ocis/issues/10329)
- [apiContract/sharesReport.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L43)
- [apiContract/sharesReport.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L84)
- [apiContract/sharesReport.feature:150](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L150)
- [apiContract/sharesReport.feature:180](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L180)
- [apiContract/spacesReport.feature:16](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L16)
- [apiContract/spacesReport.feature:35](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L35)
- [apiContract/spacesReport.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L55)
- [apiContract/spacesReport.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L74)
- [apiContract/spacesSharesReport.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesSharesReport.feature#L46)
- [apiContract/spacesSharesReport.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesSharesReport.feature#L77)
- [apiSearch1/dateSearch.feature:19](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L19)
- [apiSearch1/dateSearch.feature:39](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L39)
- [apiSearch1/dateSearch.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L40)
- [apiSearch1/dateSearch.feature:41](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L41)
- [apiSearch1/dateSearch.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L42)
- [apiSearch1/dateSearch.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L43)
- [apiSearch1/dateSearch.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L44)
- [apiSearch1/dateSearch.feature:45](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L45)
- [apiSearch1/dateSearch.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L46)
- [apiSearch1/dateSearch.feature:47](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L47)
- [apiSearch1/dateSearch.feature:48](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L48)
- [apiSearch1/dateSearch.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L50)
- [apiSearch1/dateSearch.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L53)
- [apiSearch1/search.feature:113](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L113)
- [apiSearch1/search.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L176)
- [apiSearch1/search.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L280)
- [apiSearch1/search.feature:304](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L304)
- [apiSearch1/search.feature:317](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L317)
- [apiSearch1/search.feature:318](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L318)
- [apiSearch1/search.feature:319](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L319)
- [apiSearch1/search.feature:320](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L320)
- [apiSearch1/search.feature:321](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L321)
- [apiSearch1/search.feature:324](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L324)
- [apiSearch1/search.feature:356](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L356)
- [apiSearch1/search.feature:369](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L369)
- [apiSearch1/search.feature:396](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L396)
- [apiSearch1/search.feature:410](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L410)
- [apiSearch2/mediaTypeSearch.feature:31](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L31)
- [apiSearch2/mediaTypeSearch.feature:32](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L32)
- [apiSearch2/mediaTypeSearch.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L33)
- [apiSearch2/mediaTypeSearch.feature:34](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L34)
- [apiSearch2/mediaTypeSearch.feature:35](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L35)
- [apiSearch2/mediaTypeSearch.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L36)
- [apiSearch2/mediaTypeSearch.feature:37](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L37)
- [apiSearch2/mediaTypeSearch.feature:38](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L38)
- [apiSearch2/mediaTypeSearch.feature:39](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L39)
- [apiSearch2/mediaTypeSearch.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L60)
- [apiSearch2/mediaTypeSearch.feature:61](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L61)
- [apiSearch2/mediaTypeSearch.feature:62](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L62)
- [apiSearch2/mediaTypeSearch.feature:63](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L63)
- [apiSearch2/mediaTypeSearch.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L64)
- [apiSearch2/mediaTypeSearch.feature:65](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L65)
- [apiSearch2/mediaTypeSearch.feature:66](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L66)
- [apiSearch2/mediaTypeSearch.feature:67](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L67)
- [apiSearch2/mediaTypeSearch.feature:68](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L68)
- [apiSearch2/mediaTypeSearch.feature:90](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L90)
- [apiSearch2/mediaTypeSearch.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L91)
- [apiSearch2/mediaTypeSearch.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L92)
- [apiSearch2/mediaTypeSearch.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L93)
- [apiSearch2/mediaTypeSearch.feature:94](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L94)
- [apiSearch2/mediaTypeSearch.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L95)
- [apiSearch2/mediaTypeSearch.feature:96](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L96)
- [apiSearch2/mediaTypeSearch.feature:97](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L97)
- [apiSearch2/mediaTypeSearch.feature:98](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L98)
- [apiSearch2/mediaTypeSearch.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L126)
- [apiSearch2/mediaTypeSearch.feature:127](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L127)
- [apiSearch2/mediaTypeSearch.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L128)
- [apiSearch2/mediaTypeSearch.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L129)
- [apiSearch2/mediaTypeSearch.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L130)
- [apiSearch2/mediaTypeSearch.feature:131](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L131)
- [apiSearch2/mediaTypeSearch.feature:132](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L132)
- [apiSearch2/mediaTypeSearch.feature:133](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L133)
- [apiSearch2/mediaTypeSearch.feature:134](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L134)
- [apiSearch2/mediaTypeSearch.feature:161](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L161)
- [apiSearch2/mediaTypeSearch.feature:162](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L162)
- [apiSearch2/mediaTypeSearch.feature:163](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L163)
- [apiSearch2/mediaTypeSearch.feature:164](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L164)
- [apiSearch2/mediaTypeSearch.feature:165](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L165)
- [apiSearch2/mediaTypeSearch.feature:166](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L166)
- [apiSearch2/mediaTypeSearch.feature:167](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L167)
- [apiSearch2/mediaTypeSearch.feature:168](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L168)
- [apiSearch2/mediaTypeSearch.feature:169](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L169)
- [apiSearch2/mediaTypeSearch.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L172)
- [apiSearch2/tagSearch.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L36)
- [apiSearch2/tagSearch.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L64)
- [apiSearch2/tagSearch.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L85)
- [apiSearch2/tagSearch.feature:108](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L108)
- [apiSearch2/tagSearch.feature:138](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L138)
- [apiSearch2/tagSearch.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L172)
- [apiSearch2/tagSearch.feature:207](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L207)
- [apiSearch2/tagSearch.feature:226](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L226)
- [apiSearch2/tagSearch.feature:254](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L254)
- [apiSearch2/tagSearch.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L255)
- [apiSearch2/tagSearch.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L256)
- [apiSearch2/tagSearch.feature:257](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L257)
- [apiSearch2/tagSearch.feature:258](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L258)
- [apiSearch2/tagSearch.feature:259](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L259)
- [apiSearch2/tagSearch.feature:260](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L260)
- [apiSearch2/tagSearch.feature:261](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L261)
- [apiSearch2/tagSearch.feature:262](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L262)
- [apiSearch2/tagSearch.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L263)
- [apiSearch2/tagSearch.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L265)
- [apiSearchContent/contentSearch.feature:27](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L27)
- [apiSearchContent/contentSearch.feature:51](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L51)
- [apiSearchContent/contentSearch.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L84)
- [apiSearchContent/contentSearch.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L112)
- [apiSearchContent/contentSearch.feature:131](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L131)
- [apiSearchContent/contentSearch.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L147)
- [apiSearchContent/contentSearch.feature:163](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L163)
- [apiSearchContent/contentSearch.feature:186](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L186)
- [apiSearchContent/contentSearch.feature:215](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L215)
- [apiSearchContent/contentSearch.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L233)
- [apiSearchContent/contentSearch.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L234)
- [apiSearchContent/contentSearch.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L235)
- [apiSearchContent/contentSearch.feature:236](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L236)
- [apiSearchContent/contentSearch.feature:237](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L237)
- [apiSearchContent/contentSearch.feature:238](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L238)
- [apiSearchContent/contentSearch.feature:267](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L267)
- [cliCommands/searchReIndex.feature:15](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/searchReIndex.feature#L15)
#### [Downloading public files without remote.php returns 401 unauthorized error](https://github.com/owncloud/ocis/issues/9724)
- [apiSpaces/editPublicLinkOfSpace.feature:48](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/editPublicLinkOfSpace.feature#L48)
- [apiSpaces/editPublicLinkOfSpace.feature:49](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/editPublicLinkOfSpace.feature#L49)
- [apiSpacesShares/publicLinkDownload.feature:16](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/publicLinkDownload.feature#L16)
- [apiSpacesShares/publicLinkDownload.feature:32](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/publicLinkDownload.feature#L32)
- [apiSpacesShares/shareSpacesViaLink.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L42)
- [apiSpacesShares/shareSpacesViaLink.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L43)
- [apiSpacesShares/shareSpacesViaLink.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L44)
- [apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature#L146)
- [apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature#L147)
- [apiSharingNgLinkSharePermission/createLinkShare.feature:473](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/createLinkShare.feature#L473)
- [apiSharingNgLinkSharePermission/createLinkShare.feature:1208](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/createLinkShare.feature#L1208)
- [apiSharingNgLinkSharePermission/updateLinkShare.feature:204](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/updateLinkShare.feature#L204)
- [apiSharingNgLinkSharePermission/updateLinkShare.feature:282](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/updateLinkShare.feature#L282)
- [apiSharingNgLinkShareRoot/updateLinkShare.feature:10](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkShareRoot/updateLinkShare.feature#L10)
- [apiSharingNgLinkShareRoot/updateLinkShare.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkShareRoot/updateLinkShare.feature#L42)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:220](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L220)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:221](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L221)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L59)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L60)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:88](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L88)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:89](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L89)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:122](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L122)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L123)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:209](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L209)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:210](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L210)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:229](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L229)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:230](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L230)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:257](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L257)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:258](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L258)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:273](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L273)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:274](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L274)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L277)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:291](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L291)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:111](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L111)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L112)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:249](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L249)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:250](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L250)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:276](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L276)
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L277)
#### [Most (if not all) requests to public link share without remote.php returns 401 Unauthorized error](https://github.com/owncloud/ocis/issues/10331)
- [apiSpaces/publicLink.feature:18](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L18)
- [apiSpaces/publicLink.feature:23](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L23)
- [apiSpaces/publicLink.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L28)
- [apiSpaces/publicLink.feature:34](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L34)
- [apiSpaces/publicLink.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L40)
- [apiSpaces/uploadSpaces.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L95)
- [apiSpaces/uploadSpaces.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L112)
- [apiSpaces/uploadSpaces.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L129)
- [apiSpacesShares/shareSpacesViaLink.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L58)
- [apiDepthInfinity/propfind.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiDepthInfinity/propfind.feature#L74)
- [apiDepthInfinity/propfind.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiDepthInfinity/propfind.feature#L124)
- [apiLocks/lockFiles.feature:490](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L490)
- [apiLocks/lockFiles.feature:487](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L487)
- [apiLocks/lockFiles.feature:488](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L488)
- [apiLocks/lockFiles.feature:489](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L489)
- [apiLocks/lockFiles.feature:513](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L513)
- [apiLocks/lockFiles.feature:510](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L510)
- [apiLocks/lockFiles.feature:511](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L511)
- [apiLocks/lockFiles.feature:512](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L512)
- [apiLocks/unlockFiles.feature:320](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L320)
- [apiLocks/unlockFiles.feature:321](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L321)
- [apiLocks/unlockFiles.feature:322](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L322)
- [apiLocks/unlockFiles.feature:323](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L323)
- [apiAntivirus/antivirus.feature:114](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L114)
- [apiAntivirus/antivirus.feature:115](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L115)
- [apiAntivirus/antivirus.feature:116](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L116)
- [apiAntivirus/antivirus.feature:117](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L117)
- [apiAntivirus/antivirus.feature:118](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L118)
- [apiAntivirus/antivirus.feature:119](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L119)
- [apiAntivirus/antivirus.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L140)
- [apiAntivirus/antivirus.feature:141](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L141)
- [apiAntivirus/antivirus.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L142)
- [apiAntivirus/antivirus.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L143)
- [apiAntivirus/antivirus.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L144)
- [apiAntivirus/antivirus.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L145)
- [apiAntivirus/antivirus.feature:357](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L357)
- [apiAntivirus/antivirus.feature:358](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L358)
- [apiAntivirus/antivirus.feature:359](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L359)
- [apiCollaboration/wopi.feature:956](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L956)
- [apiCollaboration/wopi.feature:957](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L957)
- [apiCollaboration/wopi.feature:958](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L958)
- [apiCollaboration/wopi.feature:961](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L961)
- [apiCollaboration/wopi.feature:1047](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1047)
- [apiCollaboration/wopi.feature:1048](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1048)
- [apiCollaboration/wopi.feature:1049](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1049)
- [apiCollaboration/wopi.feature:1052](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1052)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:27](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L27)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L28)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L29)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:30](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L30)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L33)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L46)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:70](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L70)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L95)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:120](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L120)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:132](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L132)
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L145)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L189)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L190)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:304](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L304)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:328](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L328)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:329](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L329)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:344](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L344)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:345](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L345)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:348](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L348)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:363](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L363)
- [coreApiSharePublicLink1/createPublicLinkShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L377)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:13](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L13)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L28)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L44)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L60)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L75)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L92)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:105](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L105)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:137](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L137)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:138](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L138)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:139](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L139)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L140)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:158](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L158)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:159](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L159)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:160](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L160)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:161](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L161)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:180](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L180)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:181](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L181)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:182](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L182)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:183](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L183)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:186](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L186)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:212](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L212)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:225](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L225)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L28)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L29)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:30](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L30)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L33)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L44)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L58)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:70](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L70)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:82](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L82)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:94](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L94)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:106](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L106)
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:118](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L118)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:238](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L238)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:239](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L239)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L240)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:262](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L262)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L263)
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L264)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:308](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L308)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:309](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L309)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:310](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L310)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:333](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L333)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L334)
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:335](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L335)
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:135](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L135)
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:136](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L136)
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:137](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L137)
- [coreApiWebdavEtagPropagation2/createFolder.feature:111](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/createFolder.feature#L111)
- [coreApiWebdavEtagPropagation2/upload.feature:188](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L188)
- [coreApiWebdavEtagPropagation2/upload.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L189)
- [coreApiWebdavEtagPropagation2/upload.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L190)
- [coreApiWebdavOperations/listFiles.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L112)
- [coreApiWebdavOperations/listFiles.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L140)
- [coreApiWebdavOperations/propfind.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L40)
- [coreApiWebdavOperations/propfind.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L55)
- [coreApiWebdavOperations/propfind.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L69)
- [coreApiWebdavUpload/uploadFile.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUpload/uploadFile.feature#L376)
- [apiActivities/shareActivities.feature:1956](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/shareActivities.feature#L1956)
- [apiActivities/shareActivities.feature:2095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/shareActivities.feature#L2095)
#### [Cannot create new TUS upload resource using /webdav without remote.php - returns html instead](https://github.com/owncloud/ocis/issues/10346)
@@ -339,8 +50,8 @@
- [coreApiWebdavUploadTUS/uploadToShare.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L69)
- [coreApiWebdavUploadTUS/uploadToShare.feature:89](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L89)
#### [PROPFIND to /webdav root (old dav path) without remote.php returns html instead of xml](https://github.com/owncloud/ocis/issues/10334)
#### [PROPFIND to /webdav root (old dav path) without remote.php returns html instead of xml](https://github.com/owncloud/ocis/issues/10334)
- [coreApiAuth/webDavAuth.feature:15](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavAuth.feature#L15)
- [coreApiAuth/webDavAuth.feature:25](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavAuth.feature#L25)
- [coreApiShareManagementToShares/moveReceivedShare.feature:49](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature#L49)
@@ -350,11 +61,6 @@
- [coreApiWebdavOperations/propfind.feature:26](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L26)
- [coreApiWebdavOperations/propfind.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L36)
#### [Public cannot download file preview of unprotected (without password) link share without remote.php](https://github.com/owncloud/ocis/issues/10341)
- [coreApiWebdavPreviews/linkSharePreviews.feature:25](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/linkSharePreviews.feature#L25)
- [coreApiWebdavPreviews/linkSharePreviews.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/linkSharePreviews.feature#L54)
#### [Trying to create .. resource with /webdav root (old dav path) without remote.php returns html](https://github.com/owncloud/ocis/issues/10339)
- [coreApiWebdavCopyCreate/createFileFolder.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavCopyCreate/createFileFolder.feature#L176)
@@ -30,3 +30,4 @@ _testmain.go
cover.html
README.html
.idea
.claude/
+5
View File
@@ -53,3 +53,8 @@ linters:
- varnamelen
- wrapcheck
- wsl
- gomodguard
settings:
govet:
disable:
- inline
+7 -2
View File
@@ -51,7 +51,8 @@ Validator returns only InvalidValidationError for bad validation input, nil or V
```go
err := validate.Struct(mystruct)
validationErrors := err.(validator.ValidationErrors)
var validationErrors validator.ValidationErrors
errors.As(err, &validationErrors)
```
Usage and documentation
@@ -128,6 +129,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| url | URL String |
| http_url | HTTP(s) URL String |
| https_url | HTTPS-only URL String |
| origin | Web origin (URL with HTTP(S) scheme and host, but no path/query/fragment) |
| url_encoded | URL Encoded |
| urn_rfc2141 | Urn RFC 2141 String |
@@ -169,6 +171,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| bic_iso_9362_2014 | Business Identifier Code (ISO 9362:2014) |
| bic | Business Identifier Code (ISO 9362:2022) |
| bcp47_language_tag | Language tag (BCP 47) |
| bcp47_strict_language_tag | Language tag (BCP 47), strictly following RFC 5646 |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
@@ -222,7 +225,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| sha384 | SHA384 hash |
| sha512 | SHA512 hash |
| ripemd128 | RIPEMD-128 hash |
| ripemd128 | RIPEMD-160 hash |
| ripemd160 | RIPEMD-160 hash |
| tiger128 | TIGER128 hash |
| tiger160 | TIGER160 hash |
| tiger192 | TIGER192 hash |
@@ -250,11 +253,13 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| file | Existing File |
| filepath | File Path |
| image | Image |
| mimetype | MIME Type |
| isdefault | Is Default |
| len | Length |
| max | Maximum |
| min | Minimum |
| oneof | One Of |
| noneof | None Of |
| required | Required |
| required_if | Required If |
| required_unless | Required Unless |
+314 -94
View File
@@ -3,7 +3,6 @@ package validator
import (
"bufio"
"bytes"
"cmp"
"context"
"crypto/sha256"
"encoding/hex"
@@ -16,7 +15,9 @@ import (
"net/url"
"os"
"reflect"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@@ -141,6 +142,7 @@ var (
"http_url": isHttpURL,
"https_url": isHttpsURL,
"uri": isURI,
"origin": isOrigin,
"urn_rfc2141": isUrnRFC2141, // RFC 2141
"file": isFile,
"filepath": isFilePath,
@@ -159,6 +161,7 @@ var (
"startsnotwith": startsNotWith,
"endsnotwith": endsNotWith,
"image": isImage,
"mimetype": isMIMEType,
"isbn": isISBN,
"isbn10": isISBN10,
"isbn13": isISBN13,
@@ -217,6 +220,8 @@ var (
"unique": isUnique,
"oneof": isOneOf,
"oneofci": isOneOfCI,
"noneof": isNoneOf,
"noneofci": isNoneOfCI,
"html": isHTML,
"html_encoded": isHTMLEncoded,
"url_encoded": isURLEncoded,
@@ -240,6 +245,7 @@ var (
"iso4217": isIso4217,
"iso4217_numeric": isIso4217Numeric,
"bcp47_language_tag": isBCP47LanguageTag,
"bcp47_strict_language_tag": isBCP47StrictLanguageTag,
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
"bic_iso_9362_2014": isIsoBic2014Format,
@@ -261,6 +267,33 @@ var (
var (
oneofValsCache = map[string][]string{}
oneofValsCacheRWLock = sync.RWMutex{}
// BCP47 language tag
// according to https://www.rfc-editor.org/rfc/bcp/bcp47.txt
bcp47LanguageTagRe = regexp.MustCompile(strings.Join([]string{
// group 1:
`^(`,
// irregular
`en-gb-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|`,
`sgn-be-fr|sgn-be-nl|sgn-ch-de|`,
// regular
`art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang|`,
// privateuse
`x-[a-z0-9]{1,8}`,
`)$`,
`|`,
// langtag
`^`,
`((?:[a-z]{2,3}(?:-[a-z]{3}){0,3})|[a-z]{4}|[a-z]{5,8})`, // group 2: language
`(?:-([a-z]{4}))?`, // group 3: script
`(?:-([a-z]{2}|[0-9]{3}))?`, // group 4: region
`(?:-((?:[a-z0-9]{5,8}|[0-9][a-z0-9]{3})(?:-(?:[a-z0-9]{5,8}|[0-9][a-z0-9]{3}))*))?`, // group 5: variant
`(?:-((?:[a-wyz0-9](?:-[a-z0-9]{2,8})+)(?:-(?:[a-wyz0-9](?:-[a-z0-9]{2,8})+))*))?`, // group 6: extension
`(?:-x(?:-[a-z0-9]{1,8})+)?`,
`$`,
}, ""))
)
func parseOneOfParam2(s string) []string {
@@ -307,12 +340,8 @@ func isOneOf(fl FieldLevel) bool {
default:
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
for i := 0; i < len(vals); i++ {
if vals[i] == v {
return true
}
}
return false
return slices.Contains(vals, v)
}
// isOneOfCI is the validation function for validating if the current field's value is one of the provided string values (case insensitive).
@@ -323,13 +352,20 @@ func isOneOfCI(fl FieldLevel) bool {
if field.Kind() != reflect.String {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
v := field.String()
for _, val := range vals {
if strings.EqualFold(val, v) {
return true
}
}
return false
return slices.ContainsFunc(vals, func(val string) bool {
return strings.EqualFold(val, field.String())
})
}
// isNoneOf validates that the current field's value is not one of the provided string or integer values
func isNoneOf(fl FieldLevel) bool {
return !isOneOf(fl)
}
// isNoneOfCI validates that the current field's value is not one of the provided string values (case insensitive)
func isNoneOfCI(fl FieldLevel) bool {
return !isOneOfCI(fl)
}
// isUnique is the validation function for validating if each array|slice|map value is unique
@@ -1526,6 +1562,64 @@ func isURI(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isOrigin checks if a field value is a valid web origin URL with HTTP(S) scheme and host defined, but no path, query, or fragment.
func isOrigin(fl FieldLevel) bool {
field := fl.Field()
if field.Kind() == reflect.String {
s := field.String()
if len(s) == 0 {
return false
}
// Fragments with empty content ("#") are not detectable after parse and
// u.Fragment will be empty even if # is present in the URL.
if strings.Contains(s, "#") {
return false
}
u, err := url.Parse(s)
if err != nil {
return false
}
if u.Scheme != "http" && u.Scheme != "https" {
return false
}
if u.Path != "" || u.RawQuery != "" || u.ForceQuery || u.Fragment != "" {
return false
}
if u.User != nil {
return false
}
hostname := u.Hostname()
if hostname == "" {
return false
}
if net.ParseIP(hostname) == nil && !hostnameRegexRFC1123().MatchString(hostname) {
return false
}
portStr := u.Port()
if portStr != "" {
// Port 0 is reserved (RFC 6335) and has no valid use as an origin port.
// https://www.rfc-editor.org/rfc/rfc6335.html#section-6
port, portErr := strconv.ParseUint(portStr, 10, 16)
if portErr != nil || port == 0 {
return false
}
}
return true
}
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isURL is the validation function for validating if the current field's value is a valid URL.
func isURL(fl FieldLevel) bool {
field := fl.Field()
@@ -1635,6 +1729,63 @@ func isFile(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
func detectFileMIMEType(field reflect.Value) (string, bool) {
switch field.Kind() {
case reflect.String:
filePath := field.String()
fileInfo, err := os.Stat(filePath)
if err != nil || fileInfo.IsDir() {
return "", false
}
file, err := os.Open(filePath)
if err != nil {
return "", false
}
defer func() {
_ = file.Close()
}()
mime, err := mimetype.DetectReader(file)
if err != nil {
return "", false
}
return mime.String(), true
}
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
func matchesMIMEType(mime, expected string) bool {
expectedType, expectedSubtype, ok := strings.Cut(strings.TrimSpace(expected), "/")
if !ok || expectedType == "" || expectedSubtype == "" {
return false
}
mimeType, mimeSubtype, ok := strings.Cut(mime, "/")
if !ok || mimeType == "" || mimeSubtype == "" {
return false
}
if expectedSubtype == "*" {
return mimeType == expectedType
}
return mimeType == expectedType && mimeSubtype == expectedSubtype
}
// isMIMEType is the validation function for validating if the current field's value contains the path to a file
// whose detected MIME type matches the provided validator param in the form type/subtype or type/*.
func isMIMEType(fl FieldLevel) bool {
mime, ok := detectFileMIMEType(fl.Field())
if !ok {
return false
}
return matchesMIMEType(mime, fl.Param())
}
// isImage is the validation function for validating if the current field's value contains the path to a valid image file
func isImage(fl FieldLevel) bool {
mimetypes := map[string]bool{
@@ -1663,39 +1814,14 @@ func isImage(fl FieldLevel) bool {
"image/x-xpixmap": true,
"image/x-xwindowdump": true,
}
field := fl.Field()
switch field.Kind() {
case reflect.String:
filePath := field.String()
fileInfo, err := os.Stat(filePath)
if err != nil {
return false
}
if fileInfo.IsDir() {
return false
}
file, err := os.Open(filePath)
if err != nil {
return false
}
defer func() {
_ = file.Close()
}()
mime, err := mimetype.DetectReader(file)
if err != nil {
return false
}
if _, ok := mimetypes[mime.String()]; ok {
return true
}
mime, ok := detectFileMIMEType(fl.Field())
if !ok {
return false
}
panic(fmt.Sprintf("Bad field type %s", field.Type()))
_, ok = mimetypes[mime]
return ok
}
// isFilePath is the validation function for validating if the current field's value is a valid file path.
@@ -3081,6 +3207,150 @@ func isBCP47LanguageTag(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isBCP47StrictLanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag
// according to https://www.rfc-editor.org/rfc/bcp/bcp47.txt
func isBCP47StrictLanguageTag(fl FieldLevel) bool {
field := fl.Field()
if field.Kind() != reflect.String {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
lower := strings.ToLower(field.String())
lowerTagDash := lower + "-"
m := bcp47LanguageTagRe.FindStringSubmatch(lower)
if m == nil {
return false
}
grandfatheredOrPrivateuse := m[1]
lang := m[2]
script := m[3]
region := m[4]
variant := m[5]
extension := m[6]
if grandfatheredOrPrivateuse != "" {
return true
}
// language = 2*3ALPHA ; shortest ISO 639 code
// ["-" extlang] ; sometimes followed by
// ; extended language subtags
// / 4ALPHA ; or reserved for future use
// / 5*8ALPHA ; or registered language subtag
switch n := len(lang); {
// 2*3ALPHA "-" extlang
case strings.Contains(lang, "-"):
parts := strings.Split(lang, "-")
baseLang := parts[0]
base, err := language.ParseBase(baseLang)
if err != nil {
return false
}
// base.String() normalizes the base to the shortest code
// for the language
if base.String() != baseLang {
return false
}
for _, e := range parts[1:] {
prefixes, ok := iana_subtag_registry_extlangs[e]
if !ok {
return false
}
if len(prefixes) > 0 {
found := false
for _, p := range prefixes {
if strings.HasPrefix(lowerTagDash, p) {
found = true
break
}
}
if !found {
return false
}
}
}
// 2*3ALPHA ; shortest ISO 639 code
case n <= 3:
base, err := language.ParseBase(lang)
if err != nil {
return false
}
// base.String() normalizes the base to the shortest code
// for the language
if base.String() != lang {
return false
}
// 4ALPHA ; or reserved for future use
case n == 4:
return false
// 5*8ALPHA ; or registered language subtag
default:
// registered language subtag with 5+ characters.
// As of today there aren't any.
// https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
return false
}
// script = 4ALPHA ; ISO 15924 code
if script != "" {
_, err := language.ParseScript(script)
if err != nil {
return false
}
}
// region = 2ALPHA ; ISO 3166-1 code
// 3DIGIT ; UN M.49 code
if region != "" {
if len(region) == 2 {
_, err := language.ParseRegion(region)
if err != nil {
return false
}
} else {
// Can't use language.ParseRegion() here because not all
// UN M.49 region codes are allowed, just the subset present
// in the IANA subtag registry.
_, ok := iana_subtag_registry_m49_codes[region]
if !ok {
return false
}
}
}
// variant = 5*8alphanum ; registered variants
// / (DIGIT 3alphanum)
if variant != "" {
for v := range strings.SplitSeq(variant, "-") {
_, err := language.ParseVariant(v)
if err != nil {
return false
}
_, ok := iana_subtag_registry_variants[v]
if !ok {
return false
}
}
}
if extension != "" {
_, err := language.ParseExtension(extension)
if err != nil {
return false
}
}
return true
}
// isIsoBic2014Format is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362 2014
func isIsoBic2014Format(fl FieldLevel) bool {
bicString := fl.Field().String()
@@ -3235,59 +3505,9 @@ func isEIN(fl FieldLevel) bool {
return einRegex().MatchString(field.String())
}
func isValidateFn(fl FieldLevel) bool {
const defaultParam = `Validate`
field := fl.Field()
validateFn := cmp.Or(fl.Param(), defaultParam)
ok, err := tryCallValidateFn(field, validateFn)
if err != nil {
return false
}
return ok
}
var (
errMethodNotFound = errors.New(`method not found`)
errMethodReturnNoValues = errors.New(`method return o values (void)`)
errMethodReturnInvalidType = errors.New(`method should return invalid type`)
)
func tryCallValidateFn(field reflect.Value, validateFn string) (bool, error) {
method := field.MethodByName(validateFn)
if field.CanAddr() && !method.IsValid() {
method = field.Addr().MethodByName(validateFn)
}
if !method.IsValid() {
return false, fmt.Errorf("unable to call %q on type %q: %w",
validateFn, field.Type().String(), errMethodNotFound)
}
returnValues := method.Call([]reflect.Value{})
if len(returnValues) == 0 {
return false, fmt.Errorf("unable to use result of method %q on type %q: %w",
validateFn, field.Type().String(), errMethodReturnNoValues)
}
firstReturnValue := returnValues[0]
switch firstReturnValue.Kind() {
case reflect.Bool:
return firstReturnValue.Bool(), nil
case reflect.Interface:
errorType := reflect.TypeOf((*error)(nil)).Elem()
if firstReturnValue.Type().Implements(errorType) {
return firstReturnValue.IsNil(), nil
}
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got interface %v expect error)",
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
default:
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got %v expect error or bool)",
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
}
}
+1 -1
View File
@@ -144,7 +144,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
if v.hasTagNameFunc {
name := v.tagNameFunc(fld)
if len(name) > 0 {
if len(name) > 0 || v.omitBlankFieldNames {
customName = name
}
}
+12
View File
@@ -1175,3 +1175,15 @@ var iso3166_2 = map[string]struct{}{
"ZW-BU": {}, "ZW-HA": {}, "ZW-MA": {}, "ZW-MC": {}, "ZW-ME": {},
"ZW-MI": {}, "ZW-MN": {}, "ZW-MS": {}, "ZW-MV": {}, "ZW-MW": {},
}
// Subset of UN M.49 region codes present in the IANA Language Subtag Registry:
// https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
var iana_subtag_registry_m49_codes = map[string]struct{}{
"001": {}, "002": {}, "003": {}, "005": {}, "009": {},
"011": {}, "013": {}, "014": {}, "015": {}, "017": {},
"018": {}, "019": {}, "021": {}, "029": {}, "030": {},
"034": {}, "035": {}, "039": {}, "053": {}, "054": {},
"057": {}, "061": {}, "142": {}, "143": {}, "145": {},
"150": {}, "151": {}, "154": {}, "155": {}, "202": {},
"419": {},
}
+42
View File
@@ -184,6 +184,12 @@ so the above will become excludesall=0x7C
Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation.
}
# Build tags
The library provides a build tag for build size optimizations. If you are not using
`validateFn` you can add the `validator_novalidatefn` build tag to enabled better dead
code elimination. With this build tag, any usage of `validateFn` tags will panic.
# Baked In Validators and Tags
Here is a list of the current built in validators:
@@ -546,6 +552,24 @@ Works the same as oneof but is case insensitive and therefore only accepts strin
Usage: oneofci=red green
oneofci='red green' 'blue yellow'
# None Of
For strings, ints, and uints, noneof will ensure that the value is not one of
the values in the parameter. The parameter should be a list of values separated by whitespace.
Values may be strings or numbers. To inversely match strings with spaces in them, include the target string between single quotes.
Kind of like an 'enum'.
Usage: noneof=red green
noneof='red green' 'blue yellow'
noneof=5 7 9
# None Of Case Insensitive
Works the same as noneof but is case insensitive and therefore only accepts strings.
Usage: noneofci=red green
noneofci='red green' 'blue yellow'
# Greater Than
For numbers, this will ensure that the value is greater than the
@@ -969,6 +993,16 @@ This is done using os.Stat and github.com/gabriel-vasile/mimetype
Usage: image
# MIME type path
This validates that a string value contains a valid file path and that
the file exists on the machine and matches the provided MIME type in the
form type/subtype or type/*.
This is done using os.Stat and github.com/gabriel-vasile/mimetype
Usage: mimetype=image/png
Usage: mimetype=image/*
# File Path
This validates that a string value contains a valid file path but does not
@@ -1442,6 +1476,14 @@ More information on https://pkg.go.dev/golang.org/x/text/language
Usage: bcp47_language_tag
# BCP 47 Strict Language Tag
This validates that a string value is a valid BCP 47 language tag strictly following RFC 5646 rules,
unlike language.Parse which also accepts Unicode extensions.
see https://www.rfc-editor.org/rfc/bcp/bcp47.txt
Usage: bcp47_strict_language_tag
BIC (SWIFT code - 2022 standard)
This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362:2022.
+14
View File
@@ -24,3 +24,17 @@ func WithPrivateFieldValidation() Option {
v.privateFieldValidation = true
}
}
// WithTagNameFuncBlankOmit makes a blank return from a RegisterTagNameFunc omit
// the field from the error namespace instead of silently falling back to the
// struct field name.
//
// This was made opt-in behaviour to maintain backward compatibility with
// existing callers that rely on the fallback for error namespaces.
//
// It is recommended you enable this as it will be the default behaviour in v11+.
func WithTagNameFuncBlankOmit() Option {
return func(v *Validate) {
v.omitBlankFieldNames = true
}
}
+7 -7
View File
@@ -33,7 +33,7 @@ const (
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
uUIDRegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
@@ -56,12 +56,12 @@ const (
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9-]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
@@ -77,7 +77,7 @@ const (
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbIdRegexString = "^[a-f\\d]{24}$"
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|((\*|\d+)(\/|-)\d+)|\d+|\*) ?){5,7})`
cronRegexString = `^((@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|(([A-Za-z0-9*?][A-Za-z0-9*?/,#L-]+|[*?0-9])( +([A-Za-z0-9*?][A-Za-z0-9*?/,#L-]+|[*?0-9])){4,6}))$`
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"
+15
View File
@@ -1466,6 +1466,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return t
},
},
{
tag: "timezone",
translation: "{0} must be a valid time zone",
override: false,
},
{
tag: "postcode_iso3166_alpha2",
translation: "{0} does not match postcode format of {1} country",
@@ -1504,11 +1509,21 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid image",
override: false,
},
{
tag: "mimetype",
translation: "{0} must be a valid MIME type",
override: false,
},
{
tag: "cve",
translation: "{0} must be a valid cve identifier",
override: false,
},
{
tag: "bcp47_strict_language_tag",
translation: "{0} must be a valid BCP 47 language tag",
override: false,
},
{
tag: "validateFn",
translation: "{0} must be a valid object",
+20 -6
View File
@@ -116,7 +116,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
if ct.hasTag {
if kind == reflect.Invalid {
v.str1 = string(append(ns, cf.altName...))
v.str1 = appendAltName(ns, cf.altName)
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
@@ -138,7 +138,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
return
}
v.str1 = string(append(ns, cf.altName...))
v.str1 = appendAltName(ns, cf.altName)
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
@@ -192,7 +192,9 @@ OUTER:
// VarWithField - this allows for validating against each field within the struct against a specific value
// pretty handy in certain situations
if len(cf.name) > 0 {
ns = append(append(ns, cf.altName...), '.')
if len(cf.altName) > 0 {
ns = append(append(ns, cf.altName...), '.')
}
structNs = append(append(structNs, cf.name...), '.')
}
@@ -212,7 +214,9 @@ OUTER:
// VarWithField - this allows for validating against each field within the struct against a specific value
// pretty handy in certain situations
if len(cf.name) > 0 {
ns = append(append(ns, cf.altName...), '.')
if len(cf.altName) > 0 {
ns = append(append(ns, cf.altName...), '.')
}
structNs = append(append(structNs, cf.name...), '.')
}
@@ -409,7 +413,7 @@ OUTER:
if ct.isBlockEnd || ct.next == nil {
// if we get here, no valid 'or' value and no more tags
v.str1 = string(append(ns, cf.altName...))
v.str1 = appendAltName(ns, cf.altName)
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
@@ -468,7 +472,7 @@ OUTER:
v.ct = ct
if !ct.fn(ctx, v) {
v.str1 = string(append(ns, cf.altName...))
v.str1 = appendAltName(ns, cf.altName)
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
@@ -499,6 +503,16 @@ OUTER:
}
}
func appendAltName(ns []byte, altName string) string {
if len(altName) > 0 {
return string(append(ns, altName...))
}
if n := len(ns); n > 0 && ns[n-1] == '.' {
return string(ns[:n-1])
}
return string(ns)
}
func getValue(val reflect.Value) interface{} {
if val.CanInterface() {
return val.Interface()
+6 -5
View File
@@ -68,7 +68,7 @@ type FilterFunc func(ns []byte) bool
// CustomTypeFunc allows for overriding or adding custom field type handler functions
// field = field value of the type to return a value to be validated
// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
// example Valuer from sql driver see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
type CustomTypeFunc func(field reflect.Value) interface{}
// TagNameFunc allows for adding of a custom tag name parser
@@ -96,6 +96,7 @@ type Validate struct {
hasTagNameFunc bool
requiredStructEnabled bool
privateFieldValidation bool
omitBlankFieldNames bool
}
// New returns a new instance of 'validate' with sane defaults.
@@ -428,7 +429,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
}
// StructPartial validates the fields passed in only, ignoring all others.
// Fields may be provided in a namespaced fashion relative to the struct provided
// Fields may be provided in a namespaced fashion relative to the struct provided
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
@@ -439,7 +440,7 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) error {
// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
// validation information via context.Context
// Fields may be provided in a namespaced fashion relative to the struct provided
// Fields may be provided in a namespaced fashion relative to the struct provided
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
@@ -514,7 +515,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
}
// StructExcept validates all fields except the ones passed in.
// Fields may be provided in a namespaced fashion relative to the struct provided
// Fields may be provided in a namespaced fashion relative to the struct provided
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
@@ -525,7 +526,7 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) error {
// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
// validation information via context.Context
// Fields may be provided in a namespaced fashion relative to the struct provided
// Fields may be provided in a namespaced fashion relative to the struct provided
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
+3
View File
@@ -19,3 +19,6 @@ indent_size = 2
[.golangci.yaml]
indent_size = 2
[devenv.yaml]
indent_size = 2
+7 -3
View File
@@ -1,6 +1,10 @@
/.devenv/
/.direnv/
/.pre-commit-config.yaml
/bin/
/build/
/var/
# Devenv
.devenv*
devenv.local.nix
devenv.local.yaml
.direnv
.pre-commit-config.yaml
-294
View File
@@ -1,294 +0,0 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": [
"devenv"
],
"flake-compat": [
"devenv"
],
"git-hooks": [
"devenv"
],
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1742042642,
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
"owner": "cachix",
"repo": "cachix",
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1744876578,
"narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=",
"owner": "cachix",
"repo": "devenv",
"rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": [
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
],
"pre-commit-hooks": [
"devenv"
]
},
"locked": {
"lastModified": 1741798497,
"narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1733212471,
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1733477122,
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_4"
}
}
},
"root": "root",
"version": 7
}
-46
View File
@@ -1,46 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
};
outputs =
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
perSystem =
{ pkgs, ... }:
rec {
devenv.shells = {
default = {
languages = {
go.enable = true;
};
pre-commit.hooks = {
nixpkgs-fmt.enable = true;
};
packages = with pkgs; [
golangci-lint
];
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { };
};
ci = devenv.shells.default;
};
};
};
}
+284 -48
View File
@@ -173,6 +173,25 @@
// Public: "I made it through!"
// }
//
// # Custom Decoding with Unmarshaler
//
// Types can implement the Unmarshaler interface to control their own decoding. The interface
// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an
// alternative or companion to a DecodeHook.
//
// type TrimmedString string
//
// func (t *TrimmedString) UnmarshalMapstructure(input any) error {
// str, ok := input.(string)
// if !ok {
// return fmt.Errorf("expected string, got %T", input)
// }
// *t = TrimmedString(strings.TrimSpace(str))
// return nil
// }
//
// See the Unmarshaler interface documentation for more details.
//
// # Other Configuration
//
// mapstructure is highly configurable. See the DecoderConfig struct
@@ -218,6 +237,17 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)
// values.
type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)
// Unmarshaler is the interface implemented by types that can unmarshal
// themselves. UnmarshalMapstructure receives the input data (potentially
// transformed by DecodeHook) and should populate the receiver with the
// decoded values.
//
// The Unmarshaler interface takes precedence over the default decoding
// logic for any type (structs, slices, maps, primitives, etc.).
type Unmarshaler interface {
UnmarshalMapstructure(any) error
}
// DecoderConfig is the configuration that is used to create a new decoder
// and allows customization of various aspects of decoding.
type DecoderConfig struct {
@@ -281,6 +311,13 @@ type DecoderConfig struct {
// }
Squash bool
// Deep will map structures in slices instead of copying them
//
// type Parent struct {
// Children []Child `mapstructure:",deep"`
// }
Deep bool
// Metadata is the struct that will contain extra metadata about
// the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata
@@ -290,9 +327,15 @@ type DecoderConfig struct {
Result any
// The tag name that mapstructure reads for field names. This
// defaults to "mapstructure"
// defaults to "mapstructure". Multiple tag names can be specified
// as a comma-separated list (e.g., "yaml,json"), and the first
// matching non-empty tag will be used.
TagName string
// RootName specifies the name to use for the root element in error messages. For example:
// '<rootName>' has unset fields: <fieldName>
RootName string
// The option of the value in the tag that indicates a field should
// be squashed. This defaults to "squash".
SquashTagOption string
@@ -304,11 +347,34 @@ type DecoderConfig struct {
// MatchName is the function used to match the map key to the struct
// field name or tag. Defaults to `strings.EqualFold`. This can be used
// to implement case-sensitive tag values, support snake casing, etc.
//
// MatchName is used as a fallback comparison when the direct key lookup fails.
// See also MapFieldName for transforming field names before lookup.
MatchName func(mapKey, fieldName string) bool
// DecodeNil, if set to true, will cause the DecodeHook (if present) to run
// even if the input is nil. This can be used to provide default values.
DecodeNil bool
// MapFieldName is the function used to convert the struct field name to the map's key name.
//
// This is useful for automatically converting between naming conventions without
// explicitly tagging each field. For example, to convert Go's PascalCase field names
// to snake_case map keys:
//
// MapFieldName: func(s string) string {
// return strcase.ToSnake(s)
// }
//
// When decoding from a map to a struct, the transformed field name is used for
// the initial lookup. If not found, MatchName is used as a fallback comparison.
// Explicit struct tags always take precedence over MapFieldName.
MapFieldName func(string) string
// DisableUnmarshaler, if set to true, disables the use of the Unmarshaler
// interface. Types implementing Unmarshaler will be decoded using the
// standard struct decoding logic instead.
DisableUnmarshaler bool
}
// A Decoder takes a raw interface value and turns it into structured
@@ -445,6 +511,12 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
config.MatchName = strings.EqualFold
}
if config.MapFieldName == nil {
config.MapFieldName = func(s string) string {
return s
}
}
result := &Decoder{
config: config,
}
@@ -458,7 +530,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
// Decode decodes the given raw interface to the target pointer specified
// by the configuration.
func (d *Decoder) Decode(input any) error {
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem())
// Retain some of the original behavior when multiple errors ocurr
var joinedErr interface{ Unwrap() []error }
@@ -540,36 +612,50 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {
var err error
addMetaKey := true
switch outputKind {
case reflect.Bool:
err = d.decodeBool(name, input, outVal)
case reflect.Interface:
err = d.decodeBasic(name, input, outVal)
case reflect.String:
err = d.decodeString(name, input, outVal)
case reflect.Int:
err = d.decodeInt(name, input, outVal)
case reflect.Uint:
err = d.decodeUint(name, input, outVal)
case reflect.Float32:
err = d.decodeFloat(name, input, outVal)
case reflect.Complex64:
err = d.decodeComplex(name, input, outVal)
case reflect.Struct:
err = d.decodeStruct(name, input, outVal)
case reflect.Map:
err = d.decodeMap(name, input, outVal)
case reflect.Ptr:
addMetaKey, err = d.decodePtr(name, input, outVal)
case reflect.Slice:
err = d.decodeSlice(name, input, outVal)
case reflect.Array:
err = d.decodeArray(name, input, outVal)
case reflect.Func:
err = d.decodeFunc(name, input, outVal)
default:
// If we reached this point then we weren't able to decode it
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
// Check if the target implements Unmarshaler and use it if not disabled
unmarshaled := false
if !d.config.DisableUnmarshaler {
if unmarshaler, ok := getUnmarshaler(outVal); ok {
if err = unmarshaler.UnmarshalMapstructure(input); err != nil {
err = newDecodeError(name, err)
}
unmarshaled = true
}
}
if !unmarshaled {
switch outputKind {
case reflect.Bool:
err = d.decodeBool(name, input, outVal)
case reflect.Interface:
err = d.decodeBasic(name, input, outVal)
case reflect.String:
err = d.decodeString(name, input, outVal)
case reflect.Int:
err = d.decodeInt(name, input, outVal)
case reflect.Uint:
err = d.decodeUint(name, input, outVal)
case reflect.Float32:
err = d.decodeFloat(name, input, outVal)
case reflect.Complex64:
err = d.decodeComplex(name, input, outVal)
case reflect.Struct:
err = d.decodeStruct(name, input, outVal)
case reflect.Map:
err = d.decodeMap(name, input, outVal)
case reflect.Ptr:
addMetaKey, err = d.decodePtr(name, input, outVal)
case reflect.Slice:
err = d.decodeSlice(name, input, outVal)
case reflect.Array:
err = d.decodeArray(name, input, outVal)
case reflect.Func:
err = d.decodeFunc(name, input, outVal)
default:
// If we reached this point then we weren't able to decode it
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
}
}
// If we reached here, then we successfully decoded SOMETHING, so
@@ -668,7 +754,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {
case reflect.Uint8:
var uints []uint8
if dataKind == reflect.Array {
uints = make([]uint8, dataVal.Len(), dataVal.Len())
uints = make([]uint8, dataVal.Len())
for i := range uints {
uints[i] = dataVal.Index(i).Interface().(uint8)
}
@@ -1060,8 +1146,8 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
)
}
tagValue := f.Tag.Get(d.config.TagName)
keyName := f.Name
tagValue, _ := getTagValue(f, d.config.TagName)
keyName := d.config.MapFieldName(f.Name)
if tagValue == "" && d.config.IgnoreUntaggedFields {
continue
@@ -1070,6 +1156,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
// If Squash is set in the config, we squash the field down.
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
// If Deep is set in the config, set as default value.
deep := d.config.Deep
v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
// Determine the name of the key in the map
@@ -1078,12 +1167,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
continue
}
// If "omitempty" is specified in the tag, it ignores empty values.
if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) {
continue
}
// If "omitzero" is specified in the tag, it ignores zero values.
if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {
if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() {
continue
}
@@ -1103,7 +1192,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
)
}
} else {
if strings.Index(tagValue[index+1:], "remain") != -1 {
if strings.Contains(tagValue[index+1:], "remain") {
if v.Kind() != reflect.Map {
return newDecodeError(
name+"."+f.Name,
@@ -1118,6 +1207,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
continue
}
}
deep = deep || strings.Contains(tagValue[index+1:], "deep")
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
keyName = keyNameTagValue
}
@@ -1164,6 +1256,41 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
}
case reflect.Slice:
if deep {
var childType reflect.Type
switch v.Type().Elem().Kind() {
case reflect.Struct:
childType = reflect.TypeOf(map[string]any{})
default:
childType = v.Type().Elem()
}
sType := reflect.SliceOf(childType)
addrVal := reflect.New(sType)
vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap())
if v.Len() > 0 {
reflect.Indirect(addrVal).Set(vSlice)
err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal))
if err != nil {
return err
}
}
vSlice = reflect.Indirect(addrVal)
valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice)
break
}
// When deep mapping is not needed, fallthrough to normal copy
fallthrough
default:
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
}
@@ -1471,7 +1598,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
remain := false
// We always parse the tags cause we're looking for other tags too
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
tagParts := getTagParts(fieldType, d.config.TagName)
if len(tagParts) == 0 {
tagParts = []string{""}
}
for _, tag := range tagParts[1:] {
if tag == d.config.SquashTagOption {
squash = true
@@ -1492,6 +1622,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
if !fieldVal.IsNil() {
structs = append(structs, fieldVal.Elem().Elem())
}
case reflect.Ptr:
if fieldVal.Type().Elem().Kind() == reflect.Struct {
if fieldVal.IsNil() {
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
}
structs = append(structs, fieldVal.Elem())
} else {
errs = append(errs, newDecodeError(
name+"."+fieldType.Name,
fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()),
))
}
default:
errs = append(errs, newDecodeError(
name+"."+fieldType.Name,
@@ -1516,13 +1658,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
field, fieldValue := f.field, f.val
fieldName := field.Name
tagValue := field.Tag.Get(d.config.TagName)
tagValue, _ := getTagValue(field, d.config.TagName)
if tagValue == "" && d.config.IgnoreUntaggedFields {
continue
}
tagValue = strings.SplitN(tagValue, ",", 2)[0]
if tagValue != "" {
fieldName = tagValue
} else {
fieldName = d.config.MapFieldName(fieldName)
}
rawMapKey := reflect.ValueOf(fieldName)
@@ -1605,8 +1749,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
sort.Strings(keys)
// Improve error message when name is empty by showing the target struct type
// in the case where it is empty for embedded structs.
errorName := name
if errorName == "" {
errorName = val.Type().String()
}
errs = append(errs, newDecodeError(
name,
errorName,
fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),
))
}
@@ -1692,7 +1842,7 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
return true
}
if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside
return true
}
}
@@ -1700,13 +1850,99 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
}
func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
if v.Kind() != reflect.Ptr {
return v
}
deref := v.Elem()
derefT := deref.Type()
if isStructTypeConvertibleToMap(derefT, true, tagName) {
return deref
switch v.Elem().Kind() {
case reflect.Slice:
return v.Elem()
case reflect.Struct:
deref := v.Elem()
derefT := deref.Type()
if isStructTypeConvertibleToMap(derefT, true, tagName) {
return deref
}
return v
default:
return v
}
return v
}
func hasAnyTag(field reflect.StructField, tagName string) bool {
_, ok := getTagValue(field, tagName)
return ok
}
func getTagParts(field reflect.StructField, tagName string) []string {
tagValue, ok := getTagValue(field, tagName)
if !ok {
return nil
}
return strings.Split(tagValue, ",")
}
func getTagValue(field reflect.StructField, tagName string) (string, bool) {
for _, name := range splitTagNames(tagName) {
if tag := field.Tag.Get(name); tag != "" {
return tag, true
}
}
return "", false
}
func splitTagNames(tagName string) []string {
if tagName == "" {
return []string{"mapstructure"}
}
parts := strings.Split(tagName, ",")
result := make([]string, 0, len(parts))
for _, name := range parts {
name = strings.TrimSpace(name)
if name != "" {
result = append(result, name)
}
}
return result
}
// unmarshalerType is cached for performance
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
// getUnmarshaler checks if the value implements Unmarshaler and returns
// the Unmarshaler and a boolean indicating if it was found. It handles both
// pointer and value receivers.
func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) {
// Skip invalid or nil values
if !val.IsValid() {
return nil, false
}
switch val.Kind() {
case reflect.Pointer, reflect.Interface:
if val.IsNil() {
return nil, false
}
}
// Check pointer receiver first (most common case)
if val.CanAddr() {
ptrVal := val.Addr()
// Quick check: if no methods, can't implement any interface
if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) {
return ptrVal.Interface().(Unmarshaler), true
}
}
// Check value receiver
// Quick check: if no methods, can't implement any interface
if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) {
return val.Interface().(Unmarshaler), true
}
return nil, false
}
@@ -1,6 +1,6 @@
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")
package(default_visibility = ["//visibility:public"])
+17 -1
View File
@@ -71,6 +71,7 @@ type ServeMux struct {
streamErrorHandler StreamErrorHandlerFunc
routingErrorHandler RoutingErrorHandlerFunc
disablePathLengthFallback bool
disableHTTPMethodOverride bool
unescapingMode UnescapingMode
writeContentLength bool
disableChunkedEncoding bool
@@ -271,6 +272,19 @@ func WithDisablePathLengthFallback() ServeMuxOption {
}
}
// WithDisableHTTPMethodOverride returns a ServeMuxOption that disables the
// X-HTTP-Method-Override header handling.
//
// When this option is used, the mux will no longer allow POST requests with
// the X-HTTP-Method-Override header to override the HTTP method. The path
// length fallback (POST with application/x-www-form-urlencoded falling back
// to a matching GET handler) is not affected by this option.
func WithDisableHTTPMethodOverride() ServeMuxOption {
return func(serveMux *ServeMux) {
serveMux.disableHTTPMethodOverride = true
}
}
// WithWriteContentLength returns a ServeMuxOption to enable writing content length on non-streaming responses
func WithWriteContentLength() ServeMuxOption {
return func(serveMux *ServeMux) {
@@ -405,7 +419,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path = r.URL.RawPath
}
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && !s.disableHTTPMethodOverride && s.isPathLengthFallback(r) {
if err := r.ParseForm(); err != nil {
_, outboundMarshaler := MarshalerForRequest(s, r)
sterr := status.Error(codes.InvalidArgument, err.Error())
@@ -467,6 +481,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
HTTPStatus: http.StatusBadRequest,
Err: mse,
})
return
}
continue
}
@@ -509,6 +524,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
HTTPStatus: http.StatusBadRequest,
Err: mse,
})
return
}
continue
}
+49
View File
@@ -0,0 +1,49 @@
# GEMINI.md - gotext Project Context & Guidelines
This document serves as the primary source of truth for the `gotext` project architecture, conventions, and workflows. Adherence to these guidelines is mandatory for all development tasks.
## Project Overview
`gotext` is a GNU Gettext implementation for Go (Golang). it provides a thread-safe, hierarchical translation system that supports PO/MO files, plural forms, and context-aware translations.
## Core Architecture
The project follows a hierarchical structure for managing translations:
1. **Global Package API (`gotext.go`)**: Provides convenient, global functions (e.g., `Get`, `GetD`, `GetN`) that rely on a shared `globalConfig`.
2. **Locales (`locale.go`)**: Represent a specific language (e.g., "en_US", "es_ES"). A `Locale` container manages multiple `Domains`.
3. **Domains (`domain.go`)**: The core translation engine for a specific set of messages. It handles:
- Message retrieval (with support for plural forms and `msgctxt`).
- Header parsing (including plural-form rules).
- Thread-safety via `sync.RWMutex`.
4. **Translators (`translator.go`)**: An interface for translation sources.
- **PO (`po.go`)**: Parsers for human-readable Gettext Portable Object files.
- **MO (`mo.go`)**: Parsers for binary Gettext Machine Object files.
5. **Plural Forms (`plurals/`)**: A dedicated package for compiling and evaluating GNU Gettext plural form expressions.
## CLI Tools
- **xgotext (`cli/xgotext/`)**: A tool to scan Go source files, extract translatable strings (matching `Get`, `GetD`, etc.), and generate/update PO files.
## Development Workflows
### Branching & PRs
- **Feature Branches**: All development MUST happen in dedicated feature branches (e.g., `feature/description-of-change`).
- **Target Branch**: All Pull Requests MUST be made against the `master` branch.
- **Review Requirement**: No direct commits to `master` are allowed. All changes must be reviewed via a PR.
- **GitHub Integration**: If the `gh` CLI tool is installed and authenticated, it should be used for managing Pull Requests, Issues, and Reviews. Otherwise, these tasks must be performed manually through the GitHub web interface.
### Coding Standards
- **Idiomatic Go**: Follow standard Go conventions (`gofmt`, `go vet`).
- **Thread Safety**: Ensure all shared state (like in `Domain`) is protected by appropriate synchronization primitives.
- **Testing**:
- Every new feature or bug fix must include corresponding tests.
- Use the `fixtures/` directory for sample PO/MO files.
- Run `go test ./...` to ensure no regressions.
## Key Files & Symbols
- `gotext.go`: `Configure`, `Get`, `GetD`, `GetN`.
- `locale.go`: `Locale`, `NewLocale`, `AddDomain`.
- `domain.go`: `Domain`, `Get`, `GetN`, `parseHeaders`.
- `translator.go`: `Translator` interface.
- `plurals/compiler.go`: `Compile` function for plural expressions.
## Instructions for Gemini CLI
- Always work in a feature branch.
- Before submitting, verify changes with existing and new tests.
- Propose a PR to `master` after confirmation.
+94 -210
View File
@@ -9,258 +9,142 @@
[GNU gettext utilities](https://www.gnu.org/software/gettext) for Go.
`gotext` is a native Go implementation of the GNU Gettext utilities. It provides a thread-safe, flexible, and powerful way to handle internationalization (i18n) and localization (l10n) in your Go applications.
# Features
---
- Implements GNU gettext support in native Go.
- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) including:
- Support for multiline strings and headers.
- Support for variables inside translation strings using Go's [fmt syntax](https://golang.org/pkg/fmt/).
- Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html).
- Support for [message contexts](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html).
- Support for MO files.
- Thread-safe: This package is safe for concurrent use across multiple goroutines.
- It works with UTF-8 encoding as it's the default for Go language.
- Unit tests available.
- Language codes are automatically simplified from the form `en_UK` to `en` if the first isn't available.
- Ready to use inside Go templates.
- Objects are serializable to []byte to store them in cache.
- Support for Go Modules.
**📖 [Read the Full Documentation](https://leonelquinteros.github.io/gotext/)**
---
# License
### Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [CLI Tool (xgotext)](#cli-tool-xgotext)
- [Advanced Usage](#advanced-usage)
- [Locale Object](#using-locale-object)
- [Plural Forms](#use-plural-forms-of-translations)
- [Dynamic Variables](#using-dynamic-variables-on-translations)
- [Directory Structure](#locales-directories-structure)
- [Contributing](#contributing)
- [License](#license)
[MIT license](LICENSE)
---
## Features
- Native Go implementation of Gettext (no external dependencies).
- Full support for **PO and MO files**.
- **Pluralization rules** support via GNU Gettext plural-form expressions.
- **Message context** support (`msgctxt`).
- Thread-safe for concurrent use.
- Works with UTF-8 by default.
- Integrated CLI tool (`xgotext`) for string extraction.
- Serializable objects for caching.
- Seamless integration with Go's `text/template` and `html/template`.
# Documentation
---
Refer to package documentation at (https://pkg.go.dev/github.com/leonelquinteros/gotext)
# Installation
```
## Installation
```bash
go get github.com/leonelquinteros/gotext
```
- There are no requirements or dependencies to use this package.
- No need to install GNU gettext utilities (unless specific needs of CLI tools).
- No need for environment variables. Some naming conventions are applied but not needed.
---
# Usage examples
## Using package for single language/domain settings
For quick/simple translations you can use the package level functions directly.
## Getting Started
For a quick start, use the package-level API:
```go
package main
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// Configure package: locales path, language, and domain
gotext.Configure("/path/to/locales", "en_US", "default")
// Translate text from default domain
fmt.Println(gotext.Get("My text on 'domain-name' domain"))
// Simple translation
fmt.Println(gotext.Get("Hello, world!"))
// Translate text from a different domain without reconfigure
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
// Translation with variables
fmt.Println(gotext.Get("Hello, %s!", "Gopher"))
}
```
## Using dynamic variables on translations
For more details, see the [Getting Started Guide](docs/GETTING_STARTED.md).
All translation strings support dynamic variables to be inserted without translate.
Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string.
---
## CLI Tool (xgotext)
`gotext` includes a command-line tool to extract translatable strings from your Go source code.
**Install xgotext:**
```bash
go install github.com/leonelquinteros/gotext/cli/xgotext@latest
```
**Extract strings:**
```bash
xgotext -p . -o locales/en_US/default.po
```
See the [xgotext Documentation](docs/xgotext.md) for full usage details.
---
## Advanced Usage
### Using Locale object
For managing multiple languages or domains independently:
```go
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// Set variables
name := "John"
// Translate text with variables
fmt.Println(gotext.Get("Hi, my name is %s", name))
}
l := gotext.NewLocale("/path/to/locales", "es_UY")
l.AddDomain("default")
fmt.Println(l.Get("Translate this"))
```
## Using Locale object
When having multiple languages/domains/libraries at the same time, you can create Locale objects for each variation
so you can handle each settings on their own.
### Use plural forms of translations
`gotext` handles complex pluralization rules defined in PO headers:
```go
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Create Locale with library path and language code
l := gotext.NewLocale("/path/to/locales/root/dir", "es_UY")
// Load domain '/path/to/locales/root/dir/es_UY/default.po'
l.AddDomain("default")
// Translate text from default domain
fmt.Println(l.Get("Translate this"))
// Load different domain
l.AddDomain("translations")
// Translate text from domain
fmt.Println(l.GetD("translations", "Translate this"))
}
// GetN(singular, plural, quantity, args...)
fmt.Println(gotext.GetN("I have one apple.", "I have %d apples.", 5, 5))
```
See the [Plural Forms Guide](docs/PLURALS.md) for more examples.
This is also helpful for using inside templates (from the "text/template" package), where you can pass the Locale object to the template.
If you set the Locale object as "Loc" in the template, then the template code would look like:
```
{{ .Loc.Get "Translate this" }}
```
## Using the Po object to handle .po files and PO-formatted strings
For when you need to work with PO files and strings,
you can directly use the Po object to parse it and access the translations in there in the same way.
### Using dynamic variables on translations
Supports standard `fmt` package syntax:
```go
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Set PO content
str := `
msgid "Translate this"
msgstr "Translated text"
msgid "Another string"
msgstr ""
msgid "One with var: %s"
msgstr "This one sets the var: %s"
`
// Create Po object
po := gotext.NewPo()
po.Parse(str)
fmt.Println(po.Get("Translate this"))
}
name := "John"
fmt.Println(gotext.Get("Hi, my name is %s", name))
```
---
## Use plural forms of translations
PO format supports defining one or more plural forms for the same translation.
Relying on the PO file headers, a Plural-Forms formula can be set on the translation file
as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html)
```go
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Set PO content
str := `
msgid ""
msgstr ""
# Header below
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Translate this"
msgstr "Translated text"
msgid "Another string"
msgstr ""
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s"
`
// Create Po object
po := new(gotext.Po)
po.Parse(str)
fmt.Println(po.GetN("One with var: %s", "Several with vars: %s", 54, v))
// "This one is the plural: Variable"
}
```
# Locales directories structure
The package will assume a directories structure starting with a base path that will be provided to the package configuration
or to object constructors depending on the use, but either will use the same convention to lookup inside the base path.
Inside the base directory where will be the language directories named using the language and country 2-letter codes (en_US, es_AR, ...).
All package functions can lookup after the simplified version for each language in case the full code isn't present but the more general language code exists.
So if the language set is `en_UK`, but there is no directory named after that code and there is a directory named `en`,
all package functions will be able to resolve this generalization and provide translations for the more general library.
The language codes are assumed to be [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) codes (2-letter codes).
That said, most functions will work with any coding standard as long the directory name matches the language code set on the configuration.
Then, there can be a `LC_MESSAGES` containing all PO files or the PO files themselves.
A library directory structure can look like:
## Locales directories structure
The package expects a standard Gettext directory structure:
```
/path/to/locales
/path/to/locales/en_US
/path/to/locales/en_US/LC_MESSAGES
/path/to/locales/en_US/LC_MESSAGES/default.po
/path/to/locales/en_US/LC_MESSAGES/extras.po
/path/to/locales/en_UK
/path/to/locales/en_UK/LC_MESSAGES
/path/to/locales/en_UK/LC_MESSAGES/default.po
/path/to/locales/en_UK/LC_MESSAGES/extras.po
/path/to/locales/en_AU
/path/to/locales/en_AU/LC_MESSAGES
/path/to/locales/en_AU/LC_MESSAGES/default.po
/path/to/locales/en_AU/LC_MESSAGES/extras.po
/path/to/locales/es
/path/to/locales/es/default.po
/path/to/locales/es/extras.po
/path/to/locales/es_ES
/path/to/locales/es_ES/default.po
/path/to/locales/es_ES/extras.po
/path/to/locales/fr
/path/to/locales/fr/default.po
/path/to/locales/fr/extras.po
/en_US
/LC_MESSAGES
default.po
/es_ES
default.po
```
It supports automatic language simplification (e.g., falling back from `en_UK` to `en`).
And so on...
---
## Contributing
We welcome contributions of all kinds!
- [Contributing Guidelines](CONTRIBUTING.md)
- [Code of Conduct](CODE_OF_CONDUCT.md)
# Contribute
- Please, contribute.
- Use the package on your projects.
- Report issues on Github.
- Send pull requests for bugfixes and improvements.
- Send proposals on Github issues.
---
## License
`gotext` is released under the [MIT License](LICENSE).
+1 -1
View File
@@ -55,7 +55,7 @@ func init() {
domain: "default",
languages: []string{FallbackLocale},
library: "/usr/local/share/locale",
locales: nil,
locales: make([]*Locale, 0),
}
// Register Translator types for gob encoding
+6 -1
View File
@@ -31,12 +31,17 @@ func SimplifiedLocale(lang string) string {
// FormatString applies text formatting only when needed to parse variables.
func FormatString(str string, vars ...interface{}) string {
if len(vars) > 0 {
return fmt.Sprintf(str, vars...)
return FormatStringWithArgs(str, vars)
}
return str
}
// FormatStringWithArgs applies text formatting as a proxy for fmt.Sprintf.
func FormatStringWithArgs(str string, vars []interface{}) string {
return fmt.Sprintf(str, vars...)
}
// Appendf applies text formatting only when needed to parse variables.
func Appendf(b []byte, str string, vars ...interface{}) []byte {
if len(vars) > 0 {
+4 -4
View File
@@ -357,7 +357,7 @@ func (l *Locale) GetTranslations() map[string]*Translation {
// IsTranslated reports whether a string is translated
func (l *Locale) IsTranslated(str string) bool {
return l.IsTranslatedND(l.GetDomain(), str, 0)
return l.IsTranslatedND(l.GetDomain(), str, 1)
}
// IsTranslatedN reports whether a plural string is translated
@@ -367,7 +367,7 @@ func (l *Locale) IsTranslatedN(str string, n int) bool {
// IsTranslatedD reports whether a domain string is translated
func (l *Locale) IsTranslatedD(dom, str string) bool {
return l.IsTranslatedND(dom, str, 0)
return l.IsTranslatedND(dom, str, 1)
}
// IsTranslatedND reports whether a plural domain string is translated
@@ -387,7 +387,7 @@ func (l *Locale) IsTranslatedND(dom, str string, n int) bool {
// IsTranslatedC reports whether a context string is translated
func (l *Locale) IsTranslatedC(str, ctx string) bool {
return l.IsTranslatedNDC(l.GetDomain(), str, 0, ctx)
return l.IsTranslatedNDC(l.GetDomain(), str, 1, ctx)
}
// IsTranslatedNC reports whether a plural context string is translated
@@ -397,7 +397,7 @@ func (l *Locale) IsTranslatedNC(str string, n int, ctx string) bool {
// IsTranslatedDC reports whether a domain context string is translated
func (l *Locale) IsTranslatedDC(dom, str, ctx string) bool {
return l.IsTranslatedNDC(dom, str, 0, ctx)
return l.IsTranslatedNDC(dom, str, 1, ctx)
}
// IsTranslatedNDC reports whether a plural domain context string is translated
+39
View File
@@ -0,0 +1,39 @@
site_name: gotext
site_description: GNU gettext utilities for Go
site_author: Leonel Quinteros
repo_url: https://github.com/leonelquinteros/gotext
edit_uri: edit/master/docs/
theme:
name: material
palette:
primary: indigo
accent: indigo
features:
- navigation.tabs
- navigation.sections
- navigation.top
- search.suggest
- search.highlight
nav:
- Home: index.md
- Getting Started: GETTING_STARTED.md
- User Guides:
- Plural Forms: PLURALS.md
- xgotext CLI: xgotext.md
- Character Encoding: ENCODING.md
- Best Practices: BEST_PRACTICES.md
- API Reference: https://pkg.go.dev/github.com/leonelquinteros/gotext
- Contributing:
- Guidelines: CONTRIBUTING.md
- Code of Conduct: CODE_OF_CONDUCT.md
- License: LICENSE.md
markdown_extensions:
- admonition
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
+43 -18
View File
@@ -22,7 +22,7 @@ type match struct {
closePos int
}
var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n)`)
var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n|\(|\))`)
type testToken interface {
compile(tokens []string) (test test, err error)
@@ -161,29 +161,28 @@ func compileEquality(tokens []string, sep string, builder cmpTestBuilder) (test
if err != nil {
return test, err
}
if len(split.Left) == 1 && split.Left[0] == "n" {
if len(split.Right) != 1 {
return test, errors.New("test can only compare n to integers")
}
i, err := parseUint32(split.Right[0])
if contains(split.Left, "n") && !contains(split.Left, "%") && len(split.Left) == 1 {
i, err := parseUint32(strings.Join(split.Right, ""))
if err != nil {
// Try to compile it as a full expression if it's not a simple integer
// but for now we follow the existing pattern of expecting integers here
// unless it's a mod operation.
return test, err
}
return builder(i, false), nil
} else if len(split.Right) == 1 && split.Right[0] == "n" {
if len(split.Left) != 1 {
return test, errors.New("test can only compare n to integers")
}
i, err := parseUint32(split.Left[0])
} else if contains(split.Right, "n") && !contains(split.Right, "%") && len(split.Right) == 1 {
i, err := parseUint32(strings.Join(split.Left, ""))
if err != nil {
return test, err
}
return builder(i, true), nil
} else if contains(split.Left, "n") && contains(split.Left, "%") {
return subPipe(split.Left, split.Right, builder, false)
} else if contains(split.Right, "n") && contains(split.Right, "%") {
return subPipe(split.Right, split.Left, builder, true)
}
return test, errors.New("equality test must have 'n' as one of the two tests")
return test, errors.New("equality test must have 'n' as one of the two tests")
}
var eqToken eqStruct
@@ -353,7 +352,7 @@ func split(s string) <-chan string {
// Tokenizes a string into a list of strings, tokens grouped by parenthesis are
// not split! If the string starts with ( and ends in ), those are stripped.
func tokenize(s string) []string {
func tokenize(s string) ([]string, error) {
/*
TODO: Properly detect if the string starts with a ( and ends with a )
and that those two form a matching pair.
@@ -361,7 +360,7 @@ func tokenize(s string) []string {
Eg: (foo) -> true; (foo)(bar) -> false;
*/
if len(s) == 0 {
return []string{}
return []string{}, nil
}
if s[0] == '(' && s[len(s)-1] == ')' {
s = s[1 : len(s)-1]
@@ -372,15 +371,22 @@ func tokenize(s string) []string {
if chunk[0] == '(' && chunk[len(chunk)-1] == ')' {
ret = append(ret, chunk)
} else {
for _, token := range pat.FindAllStringSubmatch(chunk, -1) {
// Verify all characters in chunk are matched by our pattern
matches := pat.FindAllStringSubmatch(chunk, -1)
matchedLen := 0
for _, token := range matches {
ret = append(ret, token[0])
matchedLen += len(token[0])
}
if matchedLen != len(chunk) {
return nil, fmt.Errorf("invalid character in expression chunk: %s", chunk)
}
}
} else {
fmt.Printf("Empty chunk in string '%s'\n", s)
}
}
return ret
return ret, nil
}
// Compile a string containing a plural form expression to a Expression object.
@@ -406,7 +412,10 @@ func contains(haystack []string, needle string) bool {
// Compiles an expression (ternary or constant)
func compileExpression(s string) (expr Expression, err error) {
tokens := tokenize(s)
tokens, err := tokenize(s)
if err != nil {
return nil, err
}
if contains(tokens, "?") {
return ternaryToken.compile(tokens)
}
@@ -415,12 +424,28 @@ func compileExpression(s string) (expr Expression, err error) {
// Compiles a test (comparison)
func compileTest(s string) (test test, err error) {
tokens := tokenize(s)
tokens, err := tokenize(s)
if err != nil {
return nil, err
}
for _, tokenDef := range precedence {
if contains(tokens, tokenDef.op) {
return tokenDef.token.compile(tokens)
}
}
if contains(tokens, "%") {
m, err := compileMod(tokens)
if err != nil {
return nil, err
}
return pipe{
modifier: m,
action: equal{value: 0}, // default to testing for 0
}, nil
}
if len(tokens) == 1 && tokens[0] == "n" {
return equal{value: 1}, nil
}
return test, errors.New("cannot compile")
}
-96
View File
@@ -1,96 +0,0 @@
## 1.5.0
* New option `IgnoreUntaggedFields` to ignore decoding to any fields
without `mapstructure` (or the configured tag name) set [GH-277]
* New option `ErrorUnset` which makes it an error if any fields
in a target struct are not set by the decoding process. [GH-225]
* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240]
* Decoding to slice from array no longer crashes [GH-265]
* Decode nested struct pointers to map [GH-271]
* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280]
* Fix issue where fields with `,omitempty` would sometimes decode
into a map with an empty string key [GH-281]
## 1.4.3
* Fix cases where `json.Number` didn't decode properly [GH-261]
## 1.4.2
* Custom name matchers to support any sort of casing, formatting, etc. for
field names. [GH-250]
* Fix possible panic in ComposeDecodeHookFunc [GH-251]
## 1.4.1
* Fix regression where `*time.Time` value would be set to empty and not be sent
to decode hooks properly [GH-232]
## 1.4.0
* A new decode hook type `DecodeHookFuncValue` has been added that has
access to the full values. [GH-183]
* Squash is now supported with embedded fields that are struct pointers [GH-205]
* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206]
## 1.3.3
* Decoding maps from maps creates a settable value for decode hooks [GH-203]
## 1.3.2
* Decode into interface type with a struct value is supported [GH-187]
## 1.3.1
* Squash should only squash embedded structs. [GH-194]
## 1.3.0
* Added `",omitempty"` support. This will ignore zero values in the source
structure when encoding. [GH-145]
## 1.2.3
* Fix duplicate entries in Keys list with pointer values. [GH-185]
## 1.2.2
* Do not add unsettable (unexported) values to the unused metadata key
or "remain" value. [GH-150]
## 1.2.1
* Go modules checksum mismatch fix
## 1.2.0
* Added support to capture unused values in a field using the `",remain"` value
in the mapstructure tag. There is an example to showcase usage.
* Added `DecoderConfig` option to always squash embedded structs
* `json.Number` can decode into `uint` types
* Empty slices are preserved and not replaced with nil slices
* Fix panic that can occur in when decoding a map into a nil slice of structs
* Improved package documentation for godoc
## 1.1.2
* Fix error when decode hook decodes interface implementation into interface
type. [GH-140]
## 1.1.1
* Fix panic that can happen in `decodePtr`
## 1.1.0
* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133]
* Support struct to struct decoding [GH-137]
* If source map value is nil, then destination map value is nil (instead of empty)
* If source slice value is nil, then destination slice value is nil (instead of empty)
* If source pointer is nil, then destination pointer is set to nil (instead of
allocated zero value of type)
## 1.0.0
* Initial tagged stable release.
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Mitchell Hashimoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-46
View File
@@ -1,46 +0,0 @@
# mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure)
mapstructure is a Go library for decoding generic map values to structures
and vice versa, while providing helpful error handling.
This library is most useful when decoding values from some data stream (JSON,
Gob, etc.) where you don't _quite_ know the structure of the underlying data
until you read a part of it. You can therefore read a `map[string]interface{}`
and use this library to decode it into the proper underlying native Go
structure.
## Installation
Standard `go get`:
```
$ go get github.com/mitchellh/mapstructure
```
## Usage & Example
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure).
The `Decode` function has examples associated with it there.
## But Why?!
Go offers fantastic standard libraries for decoding formats such as JSON.
The standard method is to have a struct pre-created, and populate that struct
from the bytes of the encoded format. This is great, but the problem is if
you have configuration or an encoding that changes slightly depending on
specific fields. For example, consider this JSON:
```json
{
"type": "person",
"name": "Mitchell"
}
```
Perhaps we can't populate a specific structure without first reading
the "type" field from the JSON. We could always do two passes over the
decoding of the JSON (reading the "type" first, and the rest later).
However, it is much simpler to just decode this into a `map[string]interface{}`
structure, read the "type" key, then use something like this library
to decode it into the proper structure.
-279
View File
@@ -1,279 +0,0 @@
package mapstructure
import (
"encoding"
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
)
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Create variables here so we can reference them with the reflect pkg
var f1 DecodeHookFuncType
var f2 DecodeHookFuncKind
var f3 DecodeHookFuncValue
// Fill in the variables into this interface and the rest is done
// automatically using the reflect package.
potential := []interface{}{f1, f2, f3}
v := reflect.ValueOf(h)
vt := v.Type()
for _, raw := range potential {
pt := reflect.ValueOf(raw).Type()
if vt.ConvertibleTo(pt) {
return v.Convert(pt).Interface()
}
}
return nil
}
// DecodeHookExec executes the given decode hook. This should be used
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
// that took reflect.Kind instead of reflect.Type.
func DecodeHookExec(
raw DecodeHookFunc,
from reflect.Value, to reflect.Value) (interface{}, error) {
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
return f(from.Type(), to.Type(), from.Interface())
case DecodeHookFuncKind:
return f(from.Kind(), to.Kind(), from.Interface())
case DecodeHookFuncValue:
return f(from, to)
default:
return nil, errors.New("invalid decode hook signature")
}
}
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
// automatically composes multiple DecodeHookFuncs.
//
// The composed funcs are called in order, with the result of the
// previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
var err error
data := f.Interface()
newFrom := f
for _, f1 := range fs {
data, err = DecodeHookExec(f1, newFrom, t)
if err != nil {
return nil, err
}
newFrom = reflect.ValueOf(data)
}
return data, nil
}
}
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
return func(a, b reflect.Value) (interface{}, error) {
var allErrs string
var out interface{}
var err error
for _, f := range ff {
out, err = DecodeHookExec(f, a, b)
if err != nil {
allErrs += err.Error() + "\n"
continue
}
return out, nil
}
return nil, errors.New(allErrs)
}
}
// StringToSliceHookFunc returns a DecodeHookFunc that converts
// string to []string by splitting on the given sep.
func StringToSliceHookFunc(sep string) DecodeHookFunc {
return func(
f reflect.Kind,
t reflect.Kind,
data interface{}) (interface{}, error) {
if f != reflect.String || t != reflect.Slice {
return data, nil
}
raw := data.(string)
if raw == "" {
return []string{}, nil
}
return strings.Split(raw, sep), nil
}
}
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
// strings to time.Duration.
func StringToTimeDurationHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(time.Duration(5)) {
return data, nil
}
// Convert it by parsing
return time.ParseDuration(data.(string))
}
}
// StringToIPHookFunc returns a DecodeHookFunc that converts
// strings to net.IP
func StringToIPHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IP{}) {
return data, nil
}
// Convert it by parsing
ip := net.ParseIP(data.(string))
if ip == nil {
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
}
return ip, nil
}
}
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
// strings to net.IPNet
func StringToIPNetHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IPNet{}) {
return data, nil
}
// Convert it by parsing
_, net, err := net.ParseCIDR(data.(string))
return net, err
}
}
// StringToTimeHookFunc returns a DecodeHookFunc that converts
// strings to time.Time.
func StringToTimeHookFunc(layout string) DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(time.Time{}) {
return data, nil
}
// Convert it by parsing
return time.Parse(layout, data.(string))
}
}
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
// the decoder.
//
// Note that this is significantly different from the WeaklyTypedInput option
// of the DecoderConfig.
func WeaklyTypedHook(
f reflect.Kind,
t reflect.Kind,
data interface{}) (interface{}, error) {
dataVal := reflect.ValueOf(data)
switch t {
case reflect.String:
switch f {
case reflect.Bool:
if dataVal.Bool() {
return "1", nil
}
return "0", nil
case reflect.Float32:
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
case reflect.Int:
return strconv.FormatInt(dataVal.Int(), 10), nil
case reflect.Slice:
dataType := dataVal.Type()
elemKind := dataType.Elem().Kind()
if elemKind == reflect.Uint8 {
return string(dataVal.Interface().([]uint8)), nil
}
case reflect.Uint:
return strconv.FormatUint(dataVal.Uint(), 10), nil
}
}
return data, nil
}
func RecursiveStructToMapHookFunc() DecodeHookFunc {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
if f.Kind() != reflect.Struct {
return f.Interface(), nil
}
var i interface{} = struct{}{}
if t.Type() != reflect.TypeOf(&i).Elem() {
return f.Interface(), nil
}
m := make(map[string]interface{})
t.Set(reflect.ValueOf(m))
return f.Interface(), nil
}
}
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
// strings to the UnmarshalText function, when the target type
// implements the encoding.TextUnmarshaler interface
func TextUnmarshallerHookFunc() DecodeHookFuncType {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
result := reflect.New(t).Interface()
unmarshaller, ok := result.(encoding.TextUnmarshaler)
if !ok {
return data, nil
}
if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
return nil, err
}
return result, nil
}
}
-50
View File
@@ -1,50 +0,0 @@
package mapstructure
import (
"errors"
"fmt"
"sort"
"strings"
)
// Error implements the error interface and can represents multiple
// errors that occur in the course of a single decode.
type Error struct {
Errors []string
}
func (e *Error) Error() string {
points := make([]string, len(e.Errors))
for i, err := range e.Errors {
points[i] = fmt.Sprintf("* %s", err)
}
sort.Strings(points)
return fmt.Sprintf(
"%d error(s) decoding:\n\n%s",
len(e.Errors), strings.Join(points, "\n"))
}
// WrappedErrors implements the errwrap.Wrapper interface to make this
// return value more useful with the errwrap and go-multierror libraries.
func (e *Error) WrappedErrors() []error {
if e == nil {
return nil
}
result := make([]error, len(e.Errors))
for i, e := range e.Errors {
result[i] = errors.New(e)
}
return result
}
func appendErrors(errors []string, err error) []string {
switch e := err.(type) {
case *Error:
return append(errors, e.Errors...)
default:
return append(errors, e.Error())
}
}
File diff suppressed because it is too large. Load diff
+1
View File
@@ -17,3 +17,4 @@
- Thomas Boerger <thomas@webhippie.de>
- Miroslav Bauer <bauer@cesnet.cz>
- Daniel Mueller <daniel.mueller@uni-muenster.de>
- Michael Stingl <mail@michaelstingl.com>
+1 -1
View File
@@ -27,7 +27,7 @@ import (
"strconv"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace"
"github.com/opencloud-eu/reva/v2/pkg/logger"
"github.com/opencloud-eu/reva/v2/pkg/registry"
@@ -27,7 +27,7 @@ import (
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/auth/scope"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
@@ -28,7 +28,7 @@ import (
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/events"
@@ -22,7 +22,7 @@ import (
"context"
appauthpb "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appauth"
"github.com/opencloud-eu/reva/v2/pkg/appauth/manager/registry"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
@@ -28,8 +28,8 @@ import (
registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/go-viper/mapstructure/v2"
"github.com/juliangruber/go-intersect"
"github.com/mitchellh/mapstructure"
"github.com/opencloud-eu/reva/v2/pkg/app"
"github.com/opencloud-eu/reva/v2/pkg/app/provider/registry"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -24,7 +24,7 @@ import (
"google.golang.org/grpc"
registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/app"
"github.com/opencloud-eu/reva/v2/pkg/app/registry/registry"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -24,7 +24,7 @@ import (
"path/filepath"
provider "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/auth"
"github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry"
@@ -22,7 +22,7 @@ import (
"context"
registrypb "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/auth"
"github.com/opencloud-eu/reva/v2/pkg/auth/registry/registry"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -30,7 +30,7 @@ import (
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
datatx "github.com/cs3org/go-cs3apis/cs3/tx/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
txdriver "github.com/opencloud-eu/reva/v2/pkg/datatx"
txregistry "github.com/opencloud-eu/reva/v2/pkg/datatx/manager/registry"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -28,12 +28,14 @@ import (
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
"github.com/pkg/errors"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
@@ -123,20 +125,22 @@ func (s *svc) Authenticate(ctx context.Context, req *gateway.AuthenticateRequest
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, token) // TODO(jfd): hardcoded metadata key. use PerRPCCredentials?
// create home directory
createHomeRes, err := s.CreateHome(ctx, &storageprovider.CreateHomeRequest{})
if err != nil {
log.Err(err).Msg("error calling CreateHome")
return &gateway.AuthenticateResponse{
Status: status.NewInternal(ctx, "error creating user home"),
}, nil
}
if res.User.GetId().GetType() != userpb.UserType_USER_TYPE_SERVICE && res.User.GetId().GetType() != userpb.UserType_USER_TYPE_LIGHTWEIGHT {
createHomeRes, err := s.CreateHome(ctx, &storageprovider.CreateHomeRequest{})
if err != nil {
log.Err(err).Msg("error calling CreateHome")
return &gateway.AuthenticateResponse{
Status: status.NewInternal(ctx, "error creating user home"),
}, nil
}
if createHomeRes.Status.Code != rpc.Code_CODE_OK && createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS {
err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway")
log.Err(err).Msg("error calling Createhome")
return &gateway.AuthenticateResponse{
Status: status.NewInternal(ctx, "error creating user home"),
}, nil
if createHomeRes.Status.Code != rpc.Code_CODE_OK && createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS {
err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway")
log.Err(err).Msg("error calling Createhome")
return &gateway.AuthenticateResponse{
Status: status.NewInternal(ctx, "error creating user home"),
}, nil
}
}
gwRes := &gateway.AuthenticateResponse{
@@ -24,7 +24,7 @@ import (
"strings"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
@@ -28,20 +28,17 @@ import (
"strings"
"time"
"github.com/BurntSushi/toml"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
linkv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/conversions"
"github.com/rs/zerolog/log"
"google.golang.org/grpc/codes"
"github.com/golang-jwt/jwt/v5"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/conversions"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/publicshare"
@@ -51,7 +48,12 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/share"
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/BurntSushi/toml"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
)
@@ -114,6 +116,19 @@ func (s *svc) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (
}, nil
}
if u.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
return &provider.CreateHomeResponse{
Status: status.NewInvalid(ctx, "gateway: refusing to create home for service user"),
}, nil
}
if u.GetId().GetType() == userpb.UserType_USER_TYPE_LIGHTWEIGHT {
return &provider.CreateHomeResponse{
Status: status.NewInvalid(ctx, "gateway: refusing to create home for lightweight user"),
}, nil
}
quotaStr := utils.ReadPlainFromOpaque(req.Opaque, "quota")
var quota *provider.Quota
if quotaStr != "" {
@@ -24,7 +24,7 @@ import (
"sort"
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/group"
"github.com/opencloud-eu/reva/v2/pkg/group/manager/registry"
@@ -22,7 +22,7 @@ import (
"context"
"fmt"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/internal/grpc/services/helloworld/proto"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
"github.com/pkg/errors"
@@ -22,7 +22,7 @@ import (
"context"
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider"
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider/authorizer/registry"
@@ -24,7 +24,7 @@ import (
permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/permission"
"github.com/opencloud-eu/reva/v2/pkg/permission/manager/registry"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
@@ -24,7 +24,7 @@ import (
"google.golang.org/grpc"
preferencespb "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/preferences"
"github.com/opencloud-eu/reva/v2/pkg/preferences/registry"
@@ -30,7 +30,7 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/password"
"github.com/opencloud-eu/reva/v2/pkg/permission"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
@@ -33,7 +33,7 @@ import (
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -39,7 +39,7 @@ import (
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
@@ -33,7 +33,7 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/conversions"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
@@ -1045,7 +1045,16 @@ func (s *Service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRe
}
func (s *Service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) {
grants, err := s.Storage.ListGrants(ctx, req.Ref)
fs := s.Storage
if req.Opaque != nil && req.Opaque.Map != nil {
if _, ok := req.Opaque.Map["with_disabled_spaces"]; ok {
if dsfs, ok := fs.(storage.SpaceDisabledListableFS); ok {
fs = dsfs.WithDisabledSpaces()
}
}
}
grants, err := fs.ListGrants(ctx, req.Ref)
if err != nil {
var st *rpc.Status
switch err.(type) {
@@ -28,7 +28,7 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registrypb "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
@@ -24,7 +24,7 @@ import (
"path/filepath"
"sort"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"google.golang.org/grpc"
@@ -33,7 +33,7 @@ import (
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"google.golang.org/grpc"
@@ -31,7 +31,7 @@ import (
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/credential/registry"
tokenregistry "github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/token/registry"
tokenwriterregistry "github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/tokenwriter/registry"
@@ -19,7 +19,7 @@
package cors
import (
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/rhttp/global"
"github.com/rs/cors"
)
@@ -25,7 +25,7 @@ import (
"strings"
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider"
@@ -33,8 +33,8 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
iso6391 "github.com/emvi/iso-639-1"
"github.com/go-chi/chi/v5"
"github.com/go-viper/mapstructure/v2"
ua "github.com/mileusna/useragent"
"github.com/mitchellh/mapstructure"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
@@ -32,7 +32,7 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/gdexlab/go-render/render"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/opencloud-eu/reva/v2/internal/http/services/archiver/manager"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff. Show more