Compare commits

...

60 Commits

Author SHA1 Message Date
Anja Barz
6b3e6751c1 adjust frontend defaultconfig.go so it can be rendered in docusaurus 2025-05-14 13:15:12 +02:00
Ralf Haferkamp
be06000c13 Merge pull request #831 from opencloud-eu/dependabot/npm_and_yarn/services/idp/dotenv-expand-12.0.2
build(deps-dev): bump dotenv-expand from 10.0.0 to 12.0.2 in /services/idp
2025-05-14 09:04:51 +02:00
Ralf Haferkamp
b0809eab95 Merge pull request #839 from opencloud-eu/dependabot/go_modules/github.com/libregraph/lico-0.66.0
build(deps): bump github.com/libregraph/lico from 0.65.2-0.20250428103211-356e98f98457 to 0.66.0
2025-05-13 17:27:38 +02:00
dependabot[bot]
bcba82414f build(deps): bump github.com/libregraph/lico
Bumps [github.com/libregraph/lico](https://github.com/libregraph/lico) from 0.65.2-0.20250428103211-356e98f98457 to 0.66.0.
- [Changelog](https://github.com/libregraph/lico/blob/master/CHANGELOG.md)
- [Commits](https://github.com/libregraph/lico/commits/v0.66.0)

---
updated-dependencies:
- dependency-name: github.com/libregraph/lico
  dependency-version: 0.66.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-13 14:58:24 +00:00
Ralf Haferkamp
6aee5d17f7 Merge pull request #832 from opencloud-eu/dependabot/npm_and_yarn/services/idp/i18next-25.1.2
build(deps): bump i18next from 23.16.8 to 25.1.2 in /services/idp
2025-05-13 11:38:23 +02:00
dependabot[bot]
ca9ccf65af build(deps): bump i18next from 23.16.8 to 25.1.2 in /services/idp
Bumps [i18next](https://github.com/i18next/i18next) from 23.16.8 to 25.1.2.
- [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/v23.16.8...v25.1.2)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 25.1.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-13 08:57:53 +00:00
Ralf Haferkamp
0cfc7eb549 Merge pull request #833 from rhafer/natjs-kv-fork
Switch to opencloud-eu fork of the go-micro nats-js-kv plugin
2025-05-13 09:51:20 +02:00
Ralf Haferkamp
d7c87a0dd5 Merge pull request #825 from rhafer/nats-disable-tracelog
nats: Don't enable debug and trace logging by default
2025-05-13 09:50:17 +02:00
Viktor Scharf
f0971d9fe4 test for #452 (#826) 2025-05-13 09:16:44 +02:00
Ralf Haferkamp
be1e328ded Switch to opencloud-eu fork of the go-micro nats-js-kv plugin 2025-05-12 18:10:59 +02:00
Ralf Haferkamp
d2f28c3464 Merge pull request #829 from opencloud-eu/dependabot/go_modules/dario.cat/mergo-1.0.2
build(deps): bump dario.cat/mergo from 1.0.1 to 1.0.2
2025-05-12 18:08:42 +02:00
dependabot[bot]
07fa4b9aa3 build(deps-dev): bump dotenv-expand in /services/idp
Bumps [dotenv-expand](https://github.com/motdotla/dotenv-expand) from 10.0.0 to 12.0.2.
- [Changelog](https://github.com/motdotla/dotenv-expand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv-expand/compare/v10.0.0...v12.0.2)

---
updated-dependencies:
- dependency-name: dotenv-expand
  dependency-version: 12.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:47:04 +00:00
dependabot[bot]
72bc94d0c9 build(deps): bump dario.cat/mergo from 1.0.1 to 1.0.2
Bumps [dario.cat/mergo](https://github.com/imdario/mergo) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: dario.cat/mergo
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:15:37 +00:00
Ralf Haferkamp
4d52257558 nats: Don't enable debug and trace logging by default
Only enable trace and/or debug logging when the outer logger has the
log level set accordingly. Even when the though the messages where not
really logged the nats service wasted quite some cycles especially in
the trace log related code path.

Related issue: #716
2025-05-12 14:47:41 +02:00
Ralf Haferkamp
abd6773fbd Merge pull request #817 from opencloud-eu/dependabot/go_modules/golang.org/x/image-0.27.0
build(deps): bump golang.org/x/image from 0.26.0 to 0.27.0
2025-05-12 12:24:47 +02:00
Ralf Haferkamp
990426ca68 Merge pull request #815 from opencloud-eu/dependabot/go_modules/github.com/CiscoM31/godata-1.0.11
build(deps): bump github.com/CiscoM31/godata from 1.0.10 to 1.0.11
2025-05-12 12:24:22 +02:00
Ralf Haferkamp
96cbff74b7 Fix unit test for latest godata release 2025-05-12 11:47:40 +02:00
dependabot[bot]
cacba3ee39 build(deps): bump github.com/CiscoM31/godata from 1.0.10 to 1.0.11
Bumps [github.com/CiscoM31/godata](https://github.com/CiscoM31/godata) from 1.0.10 to 1.0.11.
- [Release notes](https://github.com/CiscoM31/godata/releases)
- [Commits](https://github.com/CiscoM31/godata/compare/v1.0.10...v1.0.11)

---
updated-dependencies:
- dependency-name: github.com/CiscoM31/godata
  dependency-version: 1.0.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 08:21:17 +00:00
dependabot[bot]
80926aef0a build(deps): bump golang.org/x/image from 0.26.0 to 0.27.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/image/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-version: 0.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 08:20:52 +00:00
Viktor Scharf
4ad12a842f run ci with decomposed (#782)
* build(deps): bump github.com/nats-io/nats.go from 1.41.2 to 1.42.0

Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.41.2 to 1.42.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.41.2...v1.42.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-version: 1.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

run ci with decomposed

fix

Trigger CI pipeline

Trigger CI pipeline

fix

* chnge cliCommand name

* add posix expected failures file

---------

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 09:39:25 +02:00
Viktor Scharf
32d694a3e2 collaboration posix tests (#780) 2025-05-12 08:50:10 +02:00
Alex
38169e60ae fix: show special roles at the end of the list (#806) 2025-05-10 11:23:01 +02:00
Ralf Haferkamp
1e8ddbbf46 Merge pull request #803 from opencloud-eu/dependabot/go_modules/github.com/KimMachineGun/automemlimit-0.7.2
build(deps): bump github.com/KimMachineGun/automemlimit from 0.7.1 to 0.7.2
2025-05-09 16:53:51 +02:00
Ralf Haferkamp
0e0cf3f30d Merge pull request #802 from opencloud-eu/dependabot/go_modules/golang.org/x/crypto-0.38.0
build(deps): bump golang.org/x/crypto from 0.37.0 to 0.38.0
2025-05-09 08:00:49 +02:00
dependabot[bot]
715e27f728 build(deps): bump github.com/KimMachineGun/automemlimit
Bumps [github.com/KimMachineGun/automemlimit](https://github.com/KimMachineGun/automemlimit) from 0.7.1 to 0.7.2.
- [Release notes](https://github.com/KimMachineGun/automemlimit/releases)
- [Commits](https://github.com/KimMachineGun/automemlimit/compare/v0.7.1...v0.7.2)

---
updated-dependencies:
- dependency-name: github.com/KimMachineGun/automemlimit
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 15:02:10 +00:00
dependabot[bot]
a9449a816a build(deps): bump golang.org/x/crypto from 0.37.0 to 0.38.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/crypto/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 15:02:02 +00:00
Ralf Haferkamp
db508d52b6 Merge pull request #798 from opencloud-eu/rm-oo-from-full-deployment-example
chore: remove OO from full deployment example
2025-05-08 11:38:30 +02:00
Alex Ackermann
675d310067 chore: remove OO from full deployment example 2025-05-08 11:17:06 +02:00
Alex Ackermann
094b5ee3eb chore: remove OO from full deployment example 2025-05-08 11:10:56 +02:00
Ralf Haferkamp
573e87b8c3 Merge pull request #784 from opencloud-eu/dependabot/go_modules/github.com/open-policy-agent/opa-1.4.2
build(deps): bump github.com/open-policy-agent/opa from 1.3.0 to 1.4.2
2025-05-08 08:54:32 +02:00
Ralf Haferkamp
adf9580431 Merge pull request #785 from opencloud-eu/dependabot/go_modules/golang.org/x/sync-0.14.0
build(deps): bump golang.org/x/sync from 0.13.0 to 0.14.0
2025-05-08 08:54:09 +02:00
Ralf Haferkamp
ec7643eeb7 Merge pull request #777 from opencloud-eu/dependabot/npm_and_yarn/services/idp/eslint-plugin-import-2.31.0
build(deps-dev): bump eslint-plugin-import from 2.30.0 to 2.31.0 in /services/idp
2025-05-08 08:52:29 +02:00
Michael Barz
77e538f177 chore: update collabora image (#793) 2025-05-07 16:36:38 +02:00
dependabot[bot]
ee2ca4455c build(deps): bump golang.org/x/sync from 0.13.0 to 0.14.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/sync/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-06 14:25:34 +00:00
dependabot[bot]
9b4d07e8fc build(deps): bump github.com/open-policy-agent/opa from 1.3.0 to 1.4.2
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.3.0 to 1.4.2.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v1.3.0...v1.4.2)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-version: 1.4.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-06 14:25:26 +00:00
Ralf Haferkamp
ab0821956f Merge pull request #773 from rhafer/radicale_example
deployment: Adapt opencloud_full to include radicale
2025-05-06 16:06:35 +02:00
Ralf Haferkamp
b08cfb8271 deployment: Adapt opencloud_full to include radicale 2025-05-06 13:13:34 +02:00
dependabot[bot]
abc4a8de8c build(deps-dev): bump eslint-plugin-import in /services/idp
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.30.0 to 2.31.0.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.30.0...v2.31.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-version: 2.31.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 15:45:50 +00:00
Ralf Haferkamp
9e5f0debe8 Merge pull request #776 from opencloud-eu/dependabot/go_modules/github.com/nats-io/nats.go-1.42.0
build(deps): bump github.com/nats-io/nats.go from 1.41.2 to 1.42.0
2025-05-05 17:43:59 +02:00
Ralf Haferkamp
64867a5dc8 Merge pull request #775 from opencloud-eu/dependabot/go_modules/golang.org/x/oauth2-0.30.0
build(deps): bump golang.org/x/oauth2 from 0.29.0 to 0.30.0
2025-05-05 17:43:25 +02:00
Ralf Haferkamp
eb41616a98 Merge pull request #774 from opencloud-eu/dependabot/npm_and_yarn/services/idp/i18next-http-backend-3.0.2
build(deps): bump i18next-http-backend from 2.5.2 to 3.0.2 in /services/idp
2025-05-05 17:42:49 +02:00
dependabot[bot]
f6c4d9f526 build(deps): bump github.com/nats-io/nats.go from 1.41.2 to 1.42.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.41.2 to 1.42.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.41.2...v1.42.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-version: 1.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:54:35 +00:00
dependabot[bot]
558343dacd build(deps): bump golang.org/x/oauth2 from 0.29.0 to 0.30.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:47:53 +00:00
dependabot[bot]
8824201bb4 build(deps): bump i18next-http-backend in /services/idp
Bumps [i18next-http-backend](https://github.com/i18next/i18next-http-backend) from 2.5.2 to 3.0.2.
- [Changelog](https://github.com/i18next/i18next-http-backend/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next-http-backend/compare/v2.5.2...v3.0.2)

---
updated-dependencies:
- dependency-name: i18next-http-backend
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:41:09 +00:00
Ralf Haferkamp
ab6cf63efe Merge pull request #756 from rhafer/issue/206
proxy(router): Allow to set some outgoing headers
2025-05-05 11:07:08 +02:00
Viktor Scharf
308f801f03 collaborative posix test (#672)
* collaborative posix test

* fix after review
2025-05-05 10:21:11 +02:00
Ralf Haferkamp
7c8174bb80 Merge pull request #759 from opencloud-eu/dependabot/go_modules/github.com/beevik/etree-1.5.1
build(deps): bump github.com/beevik/etree from 1.5.0 to 1.5.1
2025-05-05 09:32:22 +02:00
dependabot[bot]
c659ac6972 build(deps): bump github.com/nats-io/nats-server/v2 (#762)
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.11.2 to 2.11.3.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.11.2...v2.11.3)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-version: 2.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 16:25:47 +02:00
dependabot[bot]
fac80402bf build(deps): bump github.com/beevik/etree from 1.5.0 to 1.5.1
Bumps [github.com/beevik/etree](https://github.com/beevik/etree) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/beevik/etree/releases)
- [Changelog](https://github.com/beevik/etree/blob/main/RELEASE_NOTES.md)
- [Commits](https://github.com/beevik/etree/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: github.com/beevik/etree
  dependency-version: 1.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-30 14:58:57 +00:00
Ralf Haferkamp
dcf78f7f3d proxy(router): Allow to set some outgoing headers
This introduces the "additional_headers", "remote_user_header" and
"skip_x_access_token" config keys to allow configuring routes to
external services that require addtional headers to be set.

"remote_user_header": defines the name of a Header that will carry the
userid of the authenticated user on the outgoing request.

"additional_headers": defines a list of header names and values that will
be added to outgoing requests on matching routes.

"skip_x_access_token": when set to true the reva access token will not
be added to the outgoing request.

Needed for #206
2025-04-30 10:17:58 +02:00
Ralf Haferkamp
eb1ee57db9 fix: reset 'method' loop
The method var needs to be reset to "" when handling a route that is not
method specific.
2025-04-30 10:17:58 +02:00
Ralf Haferkamp
102e92fd73 appauth: Add token and user (with roles) to context
When successfully authenticating a user via apptoken, resolve the user's
roles and add the user and the token returned by the auth service to the
request context. Rely on the account_resolve middleware to add the reva
token to the outgoing request as the other auth middlewares do.
2025-04-30 10:17:58 +02:00
Ralf Haferkamp
95f28baa52 Use constant instead of repeating the "x-access-token" string over and over 2025-04-30 10:17:58 +02:00
Ralf Haferkamp
b41ff0f424 Remove duplicate reva/pkg/ctx import 2025-04-30 10:17:58 +02:00
Ralf Haferkamp
b05deb8a5e Merge pull request #754 from opencloud-eu/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.11.2
build(deps): bump github.com/nats-io/nats-server/v2 from 2.11.1 to 2.11.2
2025-04-29 17:48:57 +02:00
Ralf Haferkamp
0a04c7ac43 Merge pull request #753 from opencloud-eu/dependabot/go_modules/github.com/gookit/config/v2-2.2.6
build(deps): bump github.com/gookit/config/v2 from 2.2.5 to 2.2.6
2025-04-29 17:48:10 +02:00
Benedikt Kulmann
62bc7313da Merge pull request #746 from opencloud-eu/set-idp-logo-default-url
feat: set idp logo defaul url
2025-04-29 16:59:51 +02:00
dependabot[bot]
e7d4551b05 build(deps): bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.11.1 to 2.11.2.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.11.1...v2.11.2)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-version: 2.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 14:15:47 +00:00
dependabot[bot]
452d40dad7 build(deps): bump github.com/gookit/config/v2 from 2.2.5 to 2.2.6
Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.5 to 2.2.6.
- [Release notes](https://github.com/gookit/config/releases)
- [Commits](https://github.com/gookit/config/compare/v2.2.5...v2.2.6)

---
updated-dependencies:
- dependency-name: github.com/gookit/config/v2
  dependency-version: 2.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 14:15:29 +00:00
Alex Ackermann
0e758c687b feat: set idp logo defaul url 2025-04-29 09:54:25 +02:00
195 changed files with 21044 additions and 2522 deletions

View File

@@ -132,6 +132,7 @@ config = {
"suites": [
"apiGraph",
"apiServiceAvailability",
"collaborativePosix",
],
"skip": False,
"withRemotePhp": [True],
@@ -900,7 +901,7 @@ def localApiTestPipeline(ctx):
for storage in params["storages"]:
for run_with_remote_php in params["withRemotePhp"]:
pipeline = {
"name": "%s-%s%s-%s" % ("CLI" if name.startswith("cli") else "API", name, "-withoutRemotePhp" if not run_with_remote_php else "", storage),
"name": "%s-%s%s-%s" % ("CLI" if name.startswith("cli") else "API", name, "-withoutRemotePhp" if not run_with_remote_php else "", "decomposed" if name.startswith("cli") else storage),
"steps": restoreBuildArtifactCache(ctx, dirs["opencloudBinArtifact"], dirs["opencloudBinPath"]) +
(tikaService() if params["tikaNeeded"] else []) +
(waitForServices("online-offices", ["collabora:9980", "onlyoffice:443", "fakeoffice:8080"]) if params["collaborationServiceNeeded"] else []) +
@@ -931,7 +932,7 @@ def localApiTestPipeline(ctx):
def localApiTests(name, suites, storage = "decomposed", extra_environment = {}, with_remote_php = False):
test_dir = "%s/tests/acceptance" % dirs["base"]
expected_failures_file = "%s/expected-failures-localAPI-on-decomposed-storage.md" % test_dir
expected_failures_file = "%s/expected-failures-localAPI-on-%s-storage.md" % (test_dir, storage)
environment = {
"TEST_SERVER_URL": OC_URL,
@@ -939,12 +940,13 @@ def localApiTests(name, suites, storage = "decomposed", extra_environment = {},
"SEND_SCENARIO_LINE_REFERENCES": True,
"STORAGE_DRIVER": storage,
"BEHAT_SUITES": ",".join(suites),
"BEHAT_FILTER_TAGS": "~@skip&&~@skipOnGraph&&~@skipOnOpencloud-%s-Storage" % storage,
"BEHAT_FILTER_TAGS": "~@skip&&~@skipOnOpencloud-%s-Storage" % storage,
"EXPECTED_FAILURES_FILE": expected_failures_file,
"UPLOAD_DELETE_WAIT_TIME": "1" if storage == "owncloud" else 0,
"OC_WRAPPER_URL": "http://%s:5200" % OC_SERVER_NAME,
"WITH_REMOTE_PHP": with_remote_php,
"COLLABORATION_SERVICE_URL": "http://wopi-fakeoffice:9300",
"OC_STORAGE_PATH": "$HOME/.opencloud/storage/users",
}
for item in extra_environment:
@@ -1107,7 +1109,7 @@ def coreApiTests(ctx, part_number = 1, number_of_parts = 1, with_remote_php = Fa
storage = "posix"
if "[decomposed]" in ctx.build.title.lower():
storage = "decomposed"
filterTags = "~@skipOnGraph&&~@skipOnOpencloud-%s-Storage" % storage
filterTags = "~@skipOnOpencloud-%s-Storage" % storage
test_dir = "%s/tests/acceptance" % dirs["base"]
expected_failures_file = "%s/expected-failures-API-on-%s-storage.md" % (test_dir, storage)
@@ -2001,6 +2003,8 @@ def opencloudServer(storage = "decomposed", accounts_hash_difficulty = 4, depend
},
},
"commands": [
"apt-get update",
"apt-get install -y inotify-tools",
"%s init --insecure true" % dirs["opencloudBin"],
"cat $OC_CONFIG_DIR/opencloud.yaml",
"cp tests/config/woodpecker/app-registry.yaml $OC_CONFIG_DIR/app-registry.yaml",

View File

@@ -182,7 +182,7 @@ COLLABORA=:collabora.yml
# Domain of Collabora, where you can find the frontend.
# Defaults to "collabora.opencloud.test"
COLLABORA_DOMAIN=
# Domain of the wopiserver which handles OnlyOffice.
# Domain of the wopiserver which handles Collabora.
# Defaults to "wopiserver.opencloud.test"
WOPISERVER_DOMAIN=
# Admin user for Collabora.
@@ -225,19 +225,10 @@ COLLABORA_SSL_VERIFICATION=false
# Defaults to "partial"
#ANTIVIRUS_MAX_SCAN_SIZE_MODE=
# Image version of the ClamAV container.
# Defaults to "latest"
# Defaults to "latest"y
CLAMAV_DOCKER_TAG=
### OnlyOffice Settings ###
# Note: the leading colon is required to enable the service.
#ONLYOFFICE=:onlyoffice.yml
# Domain for OnlyOffice. Defaults to "onlyoffice.opencloud.test".
ONLYOFFICE_DOMAIN=
# Domain for the wopiserver which handles OnlyOffice.
WOPISERVER_ONLYOFFICE_DOMAIN=
### Inbucket Settings ###
# Inbucket is a mail catcher tool for testing purposes.
# DO NOT use in Production.
@@ -294,8 +285,23 @@ KEYCLOAK_ADMIN_PASSWORD=
# Autoprovisioning mode. Defaults to "true"
#KEYCLOAK_AUTOPROVISIONING=:keycloak-autoprovisioning.yml
### Radicale Setting ###
# Radicale is a small open-source CalDAV (calendars, to-do lists) and CardDAV (contacts) server.
# When enabled OpenCloud is configured as a reverse proxy for Radicale, providing all authenticated
# OpenCloud users access to a Personal Calendar and Addressbook
#RADICALE=:radicale.yml
# Docker image to use for the Radicale Container
#RADICALE_DOCKER_IMAGE=opencloudeu/radicale
# Docker tag to pull for the Radicale Container
#RADICALE_DOCKER_TAG=latest
# Define the storage location for the Radicale data. Set the path to a local path.
# Ensure that the configuration and data directories are owned by the user and group with ID 1000:1000.
# This matches the default user inside the container and avoids permission issues when accessing files.
# Leaving it default stores data in docker internal volumes.
#RADICALE_DATA_DIR=/your/local/radicale/data
## IMPORTANT ##
# This MUST be the last line as it assembles the supplemental compose files to be used.
# ALL supplemental configs must be added here, whether commented or not.
# Each var must either be empty or contain :path/file.yml
COMPOSE_FILE=docker-compose.yml${OPENCLOUD:-}${TIKA:-}${DECOMPOSEDS3:-}${DECOMPOSEDS3_MINIO:-}${DECOMPOSED:-}${COLLABORA:-}${MONITORING:-}${IMPORTER:-}${CLAMAV:-}${ONLYOFFICE:-}${INBUCKET:-}${EXTENSIONS:-}${UNZIP:-}${DRAWIO:-}${JSONVIEWER:-}${PROGRESSBARS:-}${EXTERNALSITES:-}${KEYCLOAK:-}${LDAP:-}${KEYCLOAK_AUTOPROVISIONING:-}${LDAP_MANAGER:-}
COMPOSE_FILE=docker-compose.yml${OPENCLOUD:-}${TIKA:-}${DECOMPOSEDS3:-}${DECOMPOSEDS3_MINIO:-}${DECOMPOSED:-}${COLLABORA:-}${MONITORING:-}${IMPORTER:-}${CLAMAV:-}${INBUCKET:-}${EXTENSIONS:-}${UNZIP:-}${DRAWIO:-}${JSONVIEWER:-}${PROGRESSBARS:-}${EXTERNALSITES:-}${KEYCLOAK:-}${LDAP:-}${KEYCLOAK_AUTOPROVISIONING:-}${LDAP_MANAGER:-}${RADICALE:-}

View File

@@ -53,7 +53,7 @@ services:
restart: always
collabora:
image: collabora/code:24.04.13.2.1
image: collabora/code:25.04.1.1.1
# release notes: https://www.collaboraonline.com/release-notes/
networks:
opencloud-net:
@@ -83,4 +83,5 @@ services:
entrypoint: ['/bin/bash', '-c']
command: ['coolconfig generate-proof-key && /start-collabora-online.sh']
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:9980/hosting/discovery" ]
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/9980 && echo -e 'GET /hosting/discovery HTTP/1.1\r\nHost: localhost:9980\r\n\r\n' >&3 && head -n 1 <&3 | grep '200 OK'"]

View File

@@ -1,7 +0,0 @@
#!/bin/sh
set -e
# we can't mount it directly because the run-document-server.sh script wants to move it
cp /etc/onlyoffice/documentserver/local.dist.json /etc/onlyoffice/documentserver/local.json
/app/ds/run-document-server.sh

View File

@@ -1,71 +0,0 @@
{
"services": {
"CoAuthoring": {
"sql": {
"type": "postgres",
"dbHost": "localhost",
"dbPort": "5432",
"dbName": "onlyoffice",
"dbUser": "onlyoffice",
"dbPass": "onlyoffice"
},
"token": {
"enable": {
"request": {
"inbox": true,
"outbox": true
},
"browser": true
},
"inbox": {
"header": "Authorization"
},
"outbox": {
"header": "Authorization"
}
},
"secret": {
"inbox": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
},
"outbox": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
},
"session": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
}
}
}
},
"rabbitmq": {
"url": "amqp://guest:guest@localhost"
},
"FileConverter": {
"converter": {
"inputLimits": [
{
"type": "docx;dotx;docm;dotm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
},
{
"type": "xlsx;xltx;xlsm;xltm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
},
{
"type": "pptx;ppsx;potx;pptm;ppsm;potm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
}
]
}
}
}

View File

@@ -19,7 +19,6 @@ directives:
- 'blob:'
- 'https://embed.diagrams.net/'
# In contrary to bash and docker the default is given after the | character
- 'https://${ONLYOFFICE_DOMAIN|onlyoffice.opencloud.test}/'
- 'https://${COLLABORA_DOMAIN|collabora.opencloud.test}/'
# This is needed for the external-sites web extension when embedding sites
- 'https://docs.opencloud.eu'
@@ -29,7 +28,6 @@ directives:
- 'blob:'
- 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps/'
# In contrary to bash and docker the default is given after the | character
- 'https://${ONLYOFFICE_DOMAIN|onlyoffice.opencloud.test}/'
- 'https://${COLLABORA_DOMAIN|collabora.opencloud.test}/'
manifest-src:
- '''self'''

View File

@@ -0,0 +1,40 @@
# This adds four additional routes to the proxy. Forwarding
# request on '/carddav/', '/caldav/' and the respective '/.well-knwown'
# endpoints to the radicale container and setting the required headers.
additional_policies:
- name: default
routes:
- endpoint: /caldav/
backend: http://radicale:5232
remote_user_header: X-Remote-User
skip_x_access_token: true
additional_headers:
- X-Script-Name: /caldav
- endpoint: /.well-known/caldav
backend: http://radicale:5232
remote_user_header: X-Remote-User
skip_x_access_token: true
additional_headers:
- X-Script-Name: /caldav
- endpoint: /carddav/
backend: http://radicale:5232
remote_user_header: X-Remote-User
skip_x_access_token: true
additional_headers:
- X-Script-Name: /carddav
- endpoint: /.well-known/carddav
backend: http://radicale:5232
remote_user_header: X-Remote-User
skip_x_access_token: true
additional_headers:
- X-Script-Name: /carddav
# To enable the radicale web UI add this rule.
# "unprotected" is True because the Web UI itself ask for
# the password.
# Also set "type" to "internal" in the config/radicale/config
# - endpoint: /caldav/.web/
# backend: http://radicale:5232/
# unprotected: true
# skip_x_access_token: true
# additional_headers:
# - X-Script-Name: /caldav

View File

@@ -0,0 +1,325 @@
# -*- mode: conf -*-
# vim:ft=cfg
# Config file for Radicale - A simple calendar server
#
# Place it into /etc/radicale/config (global)
# or ~/.config/radicale/config (user)
#
# The current values are the default ones
[server]
# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# Hostname syntax (using "getaddrinfo" to resolve to IPv4/IPv6 adress(es)): hostname:port
# For example: 0.0.0.0:9999, [::]:9999, localhost:9999
hosts = 0.0.0.0:5232
# Max parallel connections
#max_connections = 8
# Max size of request body (bytes)
#max_content_length = 100000000
# Socket timeout (seconds)
#timeout = 30
# SSL flag, enable HTTPS protocol
#ssl = False
# SSL certificate path
#certificate = /etc/ssl/radicale.cert.pem
# SSL private key
#key = /etc/ssl/radicale.key.pem
# CA certificate for validating clients. This can be used to secure
# TCP traffic between Radicale and a reverse proxy
#certificate_authority =
# SSL protocol, secure configuration: ALL -SSLv3 -TLSv1 -TLSv1.1
#protocol = (default)
# SSL ciphersuite, secure configuration: DHE:ECDHE:-NULL:-SHA (see also "man openssl-ciphers")
#ciphersuite = (default)
# script name to strip from URI if called by reverse proxy
#script_name = (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)
[encoding]
# Encoding for responding requests
#request = utf-8
# Encoding for storing local collections
#stock = utf-8
[auth]
# Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user | dovecot | ldap | oauth2 | pam | denyall
type = http_x_remote_user
# Cache logins for until expiration time
#cache_logins = false
# Expiration time for caching successful logins in seconds
#cache_successful_logins_expiry = 15
## Expiration time of caching failed logins in seconds
#cache_failed_logins_expiry = 90
# Ignore modifyTimestamp and createTimestamp attributes. Required e.g. for Authentik LDAP server
#ldap_ignore_attribute_create_modify_timestamp = false
# URI to the LDAP server
#ldap_uri = ldap://localhost
# The base DN where the user accounts have to be searched
#ldap_base = ##BASE_DN##
# The reader DN of the LDAP server
#ldap_reader_dn = CN=ldapreader,CN=Users,##BASE_DN##
# Password of the reader DN
#ldap_secret = ldapreader-secret
# Path of the file containing password of the reader DN
#ldap_secret_file = /run/secrets/ldap_password
# the attribute to read the group memberships from in the user's LDAP entry (default: not set)
#ldap_groups_attribute = memberOf
# The filter to find the DN of the user. This filter must contain a python-style placeholder for the login
#ldap_filter = (&(objectClass=person)(uid={0}))
# the attribute holding the value to be used as username after authentication
#ldap_user_attribute = cn
# Use ssl on the ldap connection
# Soon to be deprecated, use ldap_security instead
#ldap_use_ssl = False
# the encryption mode to be used: tls, starttls, default is none
#ldap_security = none
# The certificate verification mode. Works for ssl and starttls. NONE, OPTIONAL, default is REQUIRED
#ldap_ssl_verify_mode = REQUIRED
# The path to the CA file in pem format which is used to certificate the server certificate
#ldap_ssl_ca_file =
# Connection type for dovecot authentication (AF_UNIX|AF_INET|AF_INET6)
# Note: credentials are transmitted in cleartext
#dovecot_connection_type = AF_UNIX
# The path to the Dovecot client authentication socket (eg. /run/dovecot/auth-client on Fedora). Radicale must have read / write access to the socket.
#dovecot_socket = /var/run/dovecot/auth-client
# Host of via network exposed dovecot socket
#dovecot_host = localhost
# Port of via network exposed dovecot socket
#dovecot_port = 12345
# IMAP server hostname
# Syntax: address | address:port | [address]:port | imap.server.tld
#imap_host = localhost
# Secure the IMAP connection
# Value: tls | starttls | none
#imap_security = tls
# OAuth2 token endpoint URL
#oauth2_token_endpoint = <URL>
# PAM service
#pam_serivce = radicale
# PAM group user should be member of
#pam_group_membership =
# Htpasswd filename
#htpasswd_filename = /etc/radicale/users
# Htpasswd encryption method
# Value: plain | bcrypt | md5 | sha256 | sha512 | autodetect
# bcrypt requires the installation of 'bcrypt' module.
#htpasswd_encryption = autodetect
# Enable caching of htpasswd file based on size and mtime_ns
#htpasswd_cache = False
# Incorrect authentication delay (seconds)
#delay = 1
# Message displayed in the client when a password is needed
#realm = Radicale - Password Required
# Convert username to lowercase, must be true for case-insensitive auth providers
#lc_username = False
# Strip domain name from username
#strip_domain = False
[rights]
# Rights backend
# Value: authenticated | owner_only | owner_write | from_file
#type = owner_only
# File for rights management from_file
#file = /etc/radicale/rights
# Permit delete of a collection (global)
#permit_delete_collection = True
# Permit overwrite of a collection (global)
#permit_overwrite_collection = True
[storage]
# Storage backend
# Value: multifilesystem | multifilesystem_nolock
#type = multifilesystem
# Folder for storing local collections, created if not present
#filesystem_folder = /var/lib/radicale/collections
# Folder for storing cache of local collections, created if not present
# Note: only used in case of use_cache_subfolder_* options are active
# Note: can be used on multi-instance setup to cache files on local node (see below)
#filesystem_cache_folder = (filesystem_folder)
# Use subfolder 'collection-cache' for 'item' cache file structure instead of inside collection folder
# Note: can be used on multi-instance setup to cache 'item' on local node
#use_cache_subfolder_for_item = False
# Use subfolder 'collection-cache' for 'history' cache file structure instead of inside collection folder
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
#use_cache_subfolder_for_history = False
# Use subfolder 'collection-cache' for 'sync-token' cache file structure instead of inside collection folder
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
#use_cache_subfolder_for_synctoken = False
# Use last modifiction time (nanoseconds) and size (bytes) for 'item' cache instead of SHA256 (improves speed)
# Note: check used filesystem mtime precision before enabling
# Note: conversion is done on access, bulk conversion can be done offline using storage verification option: radicale --verify-storage
#use_mtime_and_size_for_item_cache = False
# Use configured umask for folder creation (not applicable for OS Windows)
# Useful value: 0077 | 0027 | 0007 | 0022
#folder_umask = (system default, usual 0022)
# Delete sync token that are older (seconds)
#max_sync_token_age = 2592000
# Skip broken item instead of triggering an exception
#skip_broken_item = True
# Command that is run after changes to storage, default is emtpy
# Supported placeholders:
# %(user)s: logged-in user
# %(cwd)s : current working directory
# %(path)s: full path of item
# Command will be executed with base directory defined in filesystem_folder
# For "git" check DOCUMENTATION.md for bootstrap instructions
# Example(test): echo \"user=%(user)s path=%(path)s cwd=%(cwd)s\"
# Example(git): git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"")
#hook =
# Create predefined user collections
#
# json format:
#
# {
# "def-addressbook": {
# "D:displayname": "Personal Address Book",
# "tag": "VADDRESSBOOK"
# },
# "def-calendar": {
# "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
# "D:displayname": "Personal Calendar",
# "tag": "VCALENDAR"
# }
# }
#
predefined_collections = {
"def-addressbook": {
"D:displayname": "Personal Address Book",
"tag": "VADDRESSBOOK"
},
"def-calendar": {
"C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
"D:displayname": "Personal Calendar",
"tag": "VCALENDAR"
}
}
[web]
# Web interface backend
# Value: none | internal
type = none
[logging]
# Threshold for the logger
# Value: debug | info | warning | error | critical
#level = info
# Don't include passwords in logs
#mask_passwords = True
# Log bad PUT request content
#bad_put_request_content = False
# Log backtrace on level=debug
#backtrace_on_debug = False
# Log request header on level=debug
#request_header_on_debug = False
# Log request content on level=debug
#request_content_on_debug = False
# Log response content on level=debug
#response_content_on_debug = False
# Log rights rule which doesn't match on level=debug
#rights_rule_doesnt_match_on_debug = False
# Log storage cache actions on level=debug
#storage_cache_actions_on_debug = False
[headers]
# Additional HTTP headers
#Access-Control-Allow-Origin = *
[hook]
# Hook types
# Value: none | rabbitmq
#type = none
#rabbitmq_endpoint =
#rabbitmq_topic =
#rabbitmq_queue_type = classic
[reporting]
# When returning a free-busy report, limit the number of returned
# occurences per event to prevent DOS attacks.
#max_freebusy_occurrence = 10000

View File

@@ -1,86 +0,0 @@
---
services:
traefik:
networks:
opencloud-net:
aliases:
- ${ONLYOFFICE_DOMAIN:-onlyoffice.opencloud.test}
- ${WOPISERVER_ONLYOFFICE_DOMAIN:-wopiserver-oo.opencloud.test}
collaboration-oo:
image: ${OC_DOCKER_IMAGE:-opencloudeu/opencloud-rolling}:${OC_DOCKER_TAG:-latest}
networks:
opencloud-net:
depends_on:
opencloud:
condition: service_started
onlyoffice:
condition: service_healthy
entrypoint:
- /bin/sh
command: [ "-c", "opencloud collaboration server" ]
environment:
COLLABORATION_GRPC_ADDR: 0.0.0.0:9301
COLLABORATION_HTTP_ADDR: 0.0.0.0:9300
MICRO_REGISTRY: "nats-js-kv"
MICRO_REGISTRY_ADDRESS: "opencloud:9233"
COLLABORATION_WOPI_SRC: https://${WOPISERVER_ONLYOFFICE_DOMAIN:-wopiserver-oo.opencloud.test}
COLLABORATION_APP_NAME: "OnlyOffice"
COLLABORATION_APP_PRODUCT: "OnlyOffice"
COLLABORATION_APP_ADDR: https://${ONLYOFFICE_DOMAIN:-onlyoffice.opencloud.test}
COLLABORATION_APP_ICON: https://${ONLYOFFICE_DOMAIN:-onlyoffice.opencloud.test}/web-apps/apps/documenteditor/main/resources/img/favicon.ico
COLLABORATION_APP_INSECURE: "${INSECURE:-true}"
COLLABORATION_CS3API_DATAGATEWAY_INSECURE: "${INSECURE:-true}"
COLLABORATION_LOG_LEVEL: ${LOG_LEVEL:-info}
COLLABORATION_APP_PROOF_DISABLE: "true"
OC_URL: https://${OC_DOMAIN:-cloud.opencloud.test}
volumes:
# configure the .env file to use own paths instead of docker internal volumes
- ${OC_CONFIG_DIR:-opencloud-config}:/etc/opencloud
labels:
- "traefik.enable=true"
- "traefik.http.routers.collaboration-oo.entrypoints=https"
- "traefik.http.routers.collaboration-oo.rule=Host(`${WOPISERVER_ONLYOFFICE_DOMAIN:-wopiserver-oo.opencloud.test}`)"
- "traefik.http.routers.collaboration-oo.tls.certresolver=http"
- "traefik.http.routers.collaboration-oo.service=collaboration-oo"
- "traefik.http.services.collaboration-oo.loadbalancer.server.port=9300"
logging:
driver: ${LOG_DRIVER:-local}
restart: always
onlyoffice:
# if you want to use oo enterprise edition, use: onlyoffice/documentserver-ee:<version>
# note, you also need to add a volume, see below
image: onlyoffice/documentserver:8.2.2
# changelog https://github.com/ONLYOFFICE/DocumentServer/releases
networks:
opencloud-net:
entrypoint:
- /bin/sh
- /entrypoint-override.sh
environment:
WOPI_ENABLED: "true"
# self-signed certificates
USE_UNAUTHORIZED_STORAGE: "${INSECURE:-false}"
volumes:
# paths are relative to the main compose file
- ./config/onlyoffice/entrypoint-override.sh:/entrypoint-override.sh
- ./config/onlyoffice/local.json:/etc/onlyoffice/documentserver/local.dist.json
# if you want to use oo enterprise edition, you need to add a volume for the license file
# for details see: Registering your Enterprise Edition version -->
# https://helpcenter.onlyoffice.com/installation/docs-enterprise-install-docker.aspx
labels:
- "traefik.enable=true"
- "traefik.http.routers.onlyoffice.entrypoints=https"
- "traefik.http.routers.onlyoffice.rule=Host(`${ONLYOFFICE_DOMAIN:-onlyoffice.opencloud.test}`)"
- "traefik.http.routers.onlyoffice.tls.certresolver=http"
- "traefik.http.routers.onlyoffice.service=onlyoffice"
- "traefik.http.services.onlyoffice.loadbalancer.server.port=80"
# websockets can't be opened when this is omitted
- "traefik.http.middlewares.onlyoffice.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.onlyoffice.middlewares=onlyoffice"
logging:
driver: ${LOG_DRIVER:-local}
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/hosting/discovery"]

View File

@@ -53,7 +53,6 @@ services:
PROXY_CSP_CONFIG_FILE_LOCATION: /etc/opencloud/csp.yaml
# these three vars are needed to the csp config file to include the web office apps and the importer
COLLABORA_DOMAIN: ${COLLABORA_DOMAIN:-collabora.opencloud.test}
ONLYOFFICE_DOMAIN: ${ONLYOFFICE_DOMAIN:-onlyoffice.opencloud.test}
COMPANION_DOMAIN: ${COMPANION_DOMAIN:-companion.opencloud.test}
# enable to allow using the banned passwords list
OC_PASSWORD_POLICY_BANNED_PASSWORDS_LIST: banned-password-list.txt

View File

@@ -0,0 +1,18 @@
---
services:
opencloud:
volumes:
# external sites needs to have additional routes configured in the proxy
- ./config/opencloud/proxy.yaml:/etc/opencloud/proxy.yaml
radicale:
image: ${RADICALE_DOCKER_IMAGE:-opencloudeu/radicale}:${RADICALE_DOCKER_TAG:-latest}
networks:
opencloud-net:
logging:
driver: ${LOG_DRIVER:-local}
restart: always
volumes:
- ./config/radicale/config:/etc/radicale/config
- ${RADICALE_DATA_DIR:-radicale-data}:/var/lib/radicale
volumes:
radicale-data:

43
go.mod
View File

@@ -3,14 +3,14 @@ module github.com/opencloud-eu/opencloud
go 1.24.1
require (
dario.cat/mergo v1.0.1
github.com/CiscoM31/godata v1.0.10
github.com/KimMachineGun/automemlimit v0.7.1
dario.cat/mergo v1.0.2
github.com/CiscoM31/godata v1.0.11
github.com/KimMachineGun/automemlimit v0.7.2
github.com/Masterminds/semver v1.5.0
github.com/MicahParks/keyfunc/v2 v2.1.0
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/bbalet/stopwords v1.0.0
github.com/beevik/etree v1.5.0
github.com/beevik/etree v1.5.1
github.com/blevesearch/bleve/v2 v2.5.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.14.1
@@ -40,7 +40,7 @@ require (
github.com/google/go-cmp v0.7.0
github.com/google/go-tika v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.5
github.com/gookit/config/v2 v2.2.6
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
github.com/invopop/validation v0.8.0
@@ -51,18 +51,18 @@ require (
github.com/kovidgoyal/imaging v1.6.4
github.com/leonelquinteros/gotext v1.7.1
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457
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.11.1
github.com/nats-io/nats.go v1.41.2
github.com/nats-io/nats-server/v2 v2.11.3
github.com/nats-io/nats.go v1.42.0
github.com/oklog/run v1.1.0
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
github.com/open-policy-agent/opa v1.3.0
github.com/open-policy-agent/opa v1.4.2
github.com/opencloud-eu/reva/v2 v2.32.0
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20240829135935-80dc00d6f5ea
@@ -96,14 +96,14 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
go.opentelemetry.io/otel/sdk v1.35.0
go.opentelemetry.io/otel/trace v1.35.0
golang.org/x/crypto v0.37.0
golang.org/x/crypto v0.38.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
golang.org/x/image v0.26.0
golang.org/x/image v0.27.0
golang.org/x/net v0.39.0
golang.org/x/oauth2 v0.29.0
golang.org/x/sync v0.13.0
golang.org/x/term v0.31.0
golang.org/x/text v0.24.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0
golang.org/x/term v0.32.0
golang.org/x/text v0.25.0
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb
google.golang.org/grpc v1.72.0
google.golang.org/protobuf v1.36.6
@@ -167,6 +167,7 @@ require (
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/ristretto v0.2.0 // indirect
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@@ -206,7 +207,7 @@ require (
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.11.2 // indirect
github.com/goccy/go-yaml v1.12.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
@@ -219,7 +220,7 @@ require (
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/gookit/goutil v0.6.18 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
@@ -261,7 +262,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/jwt/v2 v2.7.3 // indirect
github.com/nats-io/jwt/v2 v2.7.4 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
@@ -322,7 +323,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
@@ -341,10 +342,10 @@ replace github.com/egirna/icap-client => github.com/fschade/icap-client v0.0.0-2
replace github.com/unrolled/secure => github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90
replace go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3
// exclude the v2 line of go-sqlite3 which was released accidentally and prevents pulling in newer versions of go-sqlite3
// see https://github.com/mattn/go-sqlite3/issues/965 for more details
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

92
go.sum
View File

@@ -36,8 +36,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
@@ -64,12 +64,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CiscoM31/godata v1.0.10 h1:DZdJ6M8QNh4HquvDDOqNLu6h77Wl86KGK7Qlbmb90sk=
github.com/CiscoM31/godata v1.0.10/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc=
github.com/CiscoM31/godata v1.0.11 h1:w7y8twuW02LdH6mak3/GJ5i0GrCv2IoZUJVqa/g5Yeo=
github.com/CiscoM31/godata v1.0.11/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc=
github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c h1:ocsNvQ2tNHme4v/lTs17HROamc7mFzZfzWcg4m+UXN0=
github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/KimMachineGun/automemlimit v0.7.1 h1:QcG/0iCOLChjfUweIMC3YL5Xy9C3VBeNmCZHrZfJMBw=
github.com/KimMachineGun/automemlimit v0.7.1/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/KimMachineGun/automemlimit v0.7.2 h1:DyfHI7zLWmZPn2Wqdy2AgTiUvrGPmnYWgwhHXtAegX4=
github.com/KimMachineGun/automemlimit v0.7.2/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
@@ -130,8 +130,8 @@ github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo=
github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/beevik/etree v1.5.1 h1:TC3zyxYp+81wAmbsi8SWUpZCurbxa6S8RITYRSkNRwo=
github.com/beevik/etree v1.5.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -257,15 +257,15 @@ github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS3
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
github.com/dgraph-io/badger/v4 v4.6.0 h1:acOwfOOZ4p1dPRnYzvkVm7rUk2Y21TgPVepCy5dJdFQ=
github.com/dgraph-io/badger/v4 v4.6.0/go.mod h1:KSJ5VTuZNC3Sd+YhvVjk2nYua9UZnnTr/SkXvdtiPgI=
github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I=
github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4=
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@@ -438,8 +438,8 @@ github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ=
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM=
github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
@@ -552,10 +552,10 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
github.com/gookit/config/v2 v2.2.6 h1:8ZbkSr3gnFg1En8za9X3vldnZca3y3C7kaBLGsdLghE=
github.com/gookit/config/v2 v2.2.6/go.mod h1:++APDf3Ebj6mjzW1ALkegvg1evQKyx4FpuQqQZ2s2WM=
github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
@@ -690,8 +690,6 @@ github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 h1:A1xJ2NKgiYFiaHiLl9B5yw/gUBACSs9crDykTS3GuQI=
github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90 h1:pfI8Z5yavO6fU6vDGlWhZ4BgDlvj8c6xB7J57HfTPwA=
github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -720,8 +718,8 @@ github.com/leonelquinteros/gotext v1.7.1 h1:/JNPeE3lY5JeVYv2+KBpz39994W3W9fmZCGq
github.com/leonelquinteros/gotext v1.7.1/go.mod h1:I0WoFDn9u2D3VbPnnDPT8mzZu0iSXG8iih+AH2fHHqg=
github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk=
github.com/libregraph/idm v0.5.0/go.mod h1:BGMwIQ/6orJSPVzJ1x6kgG2JyG9GY05YFmbsnaD80k0=
github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457 h1:cwmUM+mSeqWYtZKAHn8QN7ns1nNf3Pc8nUfShka9+x0=
github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457/go.mod h1:2s2UkO0pY7/k1UlenXwio1qenfHZ217Npx22YyZJfSA=
github.com/libregraph/lico v0.66.0 h1:7T6fD1YF0Ep9n0g4KN6dvWHTlDC3awrQpgsP5GdYCF4=
github.com/libregraph/lico v0.66.0/go.mod h1:QI7NfmAkAWQ2y97iVfLv10S8tcvPQjc630uyfHGjIOw=
github.com/libregraph/oidc-go v1.1.0 h1:RyudjL3UyQblqeBQI06W53PniWobqODeeyAy6v/HumA=
github.com/libregraph/oidc-go v1.1.0/go.mod h1:qW9ubcXvZrfbbWZBaLMuk7bt5qAUMYyt9/NtXQt07Cw=
github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=
@@ -820,12 +818,12 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE=
github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4=
github.com/nats-io/nats-server/v2 v2.11.1 h1:LwdauqMqMNhTxTN3+WFTX6wGDOKntHljgZ+7gL5HCnk=
github.com/nats-io/nats-server/v2 v2.11.1/go.mod h1:leXySghbdtXSUmWem8K9McnJ6xbJOb0t9+NQ5HTRZjI=
github.com/nats-io/nats.go v1.41.2 h1:5UkfLAtu/036s99AhFRlyNDI1Ieylb36qbGjJzHixos=
github.com/nats-io/nats.go v1.41.2/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/jwt/v2 v2.7.4 h1:jXFuDDxs/GQjGDZGhNgH4tXzSUK6WQi2rsj4xmsNOtI=
github.com/nats-io/jwt/v2 v2.7.4/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
github.com/nats-io/nats-server/v2 v2.11.3 h1:AbGtXxuwjo0gBroLGGr/dE0vf24kTKdRnBq/3z/Fdoc=
github.com/nats-io/nats-server/v2 v2.11.3/go.mod h1:6Z6Fd+JgckqzKig7DYwhgrE7bJ6fypPHnGPND+DqgMY=
github.com/nats-io/nats.go v1.42.0 h1:ynIMupIOvf/ZWH/b2qda6WGKGNSjwOUutTpWRvAmhaM=
github.com/nats-io/nats.go v1.42.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
@@ -858,8 +856,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/open-policy-agent/opa v1.3.0 h1:zVvQvQg+9+FuSRBt4LgKNzJwsWl/c85kD5jPozJTydY=
github.com/open-policy-agent/opa v1.3.0/go.mod h1:t9iPNhaplD2qpiBqeudzJtEX3fKHK8zdA29oFvofAHo=
github.com/open-policy-agent/opa v1.4.2 h1:ag4upP7zMsa4WE2p1pwAFeG4Pn3mNwfAx9DLhhJfbjU=
github.com/open-policy-agent/opa v1.4.2/go.mod h1:DNzZPKqKh4U0n0ANxcCVlw8lCSv2c+h5G/3QvSYdWZ8=
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a h1:Sakl76blJAaM6NxylVkgSzktjo2dS504iDotEFJsh3M=
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/reva/v2 v2.32.0 h1:JRWPleHiEl0film95Gkh1iBEhc6eikEsx5FKLfVx6l8=
github.com/opencloud-eu/reva/v2 v2.32.0/go.mod h1:FDhGVC+ZsRRWdC3am4EbuILBtviTbCDVrTUjFECOqvg=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -1223,8 +1223,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
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=
@@ -1240,8 +1240,8 @@ golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScy
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1331,8 +1331,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1350,8 +1350,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1432,8 +1432,8 @@ 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.20.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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -1445,8 +1445,8 @@ 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.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
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=
@@ -1462,8 +1462,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@@ -42,7 +42,7 @@ func ExtractAccountUUID(opts ...account.Option) func(http.Handler) http.Handler
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("x-access-token")
token := r.Header.Get(revactx.TokenHeader)
if len(token) == 0 {
roleIDsJSON, _ := json.Marshal([]string{})
ctx := metadata.Set(r.Context(), RoleIDs, string(roleIDsJSON))

View File

@@ -45,7 +45,7 @@ func (s *ActivitylogService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// HandleGetItemActivities handles the request to get the activities of an item.
func (s *ActivitylogService) HandleGetItemActivities(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, r.Header.Get("X-Access-Token"))
ctx = metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, r.Header.Get(revactx.TokenHeader))
activeUser, ok := revactx.ContextGetUser(ctx)
if !ok {

View File

@@ -278,7 +278,7 @@ func (a *AuthAppService) authenticateUser(userID, userName string, gwc gateway.G
func getContext(r *http.Request) context.Context {
ctx := r.Context()
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, r.Header.Get("X-Access-Token"))
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, r.Header.Get(ctxpkg.TokenHeader))
}
func buildClientID(userID, userName string) string {

View File

@@ -108,7 +108,7 @@ func DefaultConfig() *config.Config {
OCS: config.OCS{
Prefix: "ocs",
SharePrefix: "/Shares",
HomeNamespace: "/users/{{.Id.OpaqueId}}",
HomeNamespace: "/users/`{{.Id.OpaqueId}}`",
AdditionalInfoAttribute: "{{.Mail}}",
StatCacheType: "memory",
StatCacheNodes: []string{"127.0.0.1:9233"},

View File

@@ -11,7 +11,6 @@ import (
opkgm "github.com/opencloud-eu/opencloud/pkg/middleware"
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
"github.com/opencloud-eu/reva/v2/pkg/auth/scope"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/token/manager/jwt"
)
@@ -43,7 +42,7 @@ func Auth(opts ...account.Option) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
t := r.Header.Get("x-access-token")
t := r.Header.Get(revactx.TokenHeader)
if t == "" {
errorcode.InvalidAuthenticationToken.Render(w, r, http.StatusUnauthorized, "Access token is empty.")
/* msgraph error for GET https://graph.microsoft.com/v1.0/me
@@ -84,10 +83,10 @@ func Auth(opts ...account.Option) func(http.Handler) http.Handler {
}
ctx = metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, t)
initiatorID := r.Header.Get(ctxpkg.InitiatorHeader)
initiatorID := r.Header.Get(revactx.InitiatorHeader)
if initiatorID != "" {
ctx = ctxpkg.ContextSetInitiator(ctx, initiatorID)
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.InitiatorHeader, initiatorID)
ctx = revactx.ContextSetInitiator(ctx, initiatorID)
ctx = metadata.AppendToOutgoingContext(ctx, revactx.InitiatorHeader, initiatorID)
}
next.ServeHTTP(w, r.WithContext(ctx))

View File

@@ -496,7 +496,8 @@ var _ = Describe("Users", func() {
},
Entry("with invalid filter", "invalid", http.StatusBadRequest),
Entry("with unsupported filter for user property", "mail eq 'unsupported'", http.StatusNotImplemented),
Entry("with unsupported filter operation", "mail add 10", http.StatusNotImplemented),
// This error is caugh by godata's parser already
Entry("with unsupported filter operation", "mail add 10", http.StatusBadRequest),
Entry("with unsupported logical operation", "memberOf/any(n:n/id eq 1) or memberOf/any(n:n/id eq 2)", http.StatusNotImplemented),
Entry("with unsupported lambda query ", `drives/any(n:n/id eq '1')`, http.StatusNotImplemented),
Entry("with unsupported lambda token ", "memberOf/all(n:n/id eq 1)", http.StatusNotImplemented),

View File

@@ -226,7 +226,28 @@ var (
Condition: proto.String(UnifiedRoleConditionFolderFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(10),
}
}()
// roleSecureViewer creates a secure viewer role
roleSecureViewer = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSecureViewerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSecureViewerID),
Description: proto.String(_secureViewerUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFile),
},
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFolder),
},
},
LibreGraphWeight: proto.Int32(20),
}
}()
@@ -255,7 +276,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFolderFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(30),
}
}()
@@ -272,7 +293,24 @@ var (
Condition: proto.String(UnifiedRoleConditionDrive),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(40),
}
}()
// roleEditorLite creates an editor-lite role
roleEditorLite = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewEditorLiteRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleEditorLiteID),
Description: proto.String(_editorLiteUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFolder),
},
},
LibreGraphWeight: proto.Int32(50),
}
}()
@@ -293,7 +331,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFolderFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(60),
}
}()
@@ -314,24 +352,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFolderFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
}
}()
// roleSpaceEditor creates an editor role
roleSpaceEditor = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSpaceEditorRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSpaceEditorID),
Description: proto.String(_spaceEditorUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionDrive),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(70),
}
}()
@@ -348,7 +369,24 @@ var (
Condition: proto.String(UnifiedRoleConditionDrive),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(80),
}
}()
// roleSpaceEditor creates an editor role
roleSpaceEditor = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSpaceEditorRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSpaceEditorID),
Description: proto.String(_spaceEditorUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionDrive),
},
},
LibreGraphWeight: proto.Int32(90),
}
}()
@@ -369,7 +407,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFileFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(100),
}
}()
@@ -390,24 +428,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFileFederatedUser),
},
},
LibreGraphWeight: proto.Int32(0),
}
}()
// roleEditorLite creates an editor-lite role
roleEditorLite = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewEditorLiteRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleEditorLiteID),
Description: proto.String(_editorLiteUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFolder),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(110),
}
}()
@@ -424,30 +445,10 @@ var (
Condition: proto.String(UnifiedRoleConditionDrive),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(120),
}
}()
// roleSecureViewer creates a secure viewer role
roleSecureViewer = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSecureViewerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSecureViewerID),
Description: proto.String(_secureViewerUnifiedRoleDescription),
DisplayName: proto.String(cs3RoleToDisplayName(r)),
RolePermissions: []libregraph.UnifiedRolePermission{
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFile),
},
{
AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()),
Condition: proto.String(UnifiedRoleConditionFolder),
},
},
LibreGraphWeight: proto.Int32(0),
}
}()
// roleDenied creates a secure viewer role
roleDenied = func() *libregraph.UnifiedRoleDefinition {
r := conversions.NewDeniedRole()
@@ -461,7 +462,7 @@ var (
Condition: proto.String(UnifiedRoleConditionFolder),
},
},
LibreGraphWeight: proto.Int32(0),
LibreGraphWeight: proto.Int32(200),
}
}()
)
@@ -531,39 +532,17 @@ func GetLegacyRoleName(role libregraph.UnifiedRoleDefinition) string {
return legacyNames[role.GetId()]
}
// weightRoles sorts the provided role definitions by the number of permissions[n].actions they grant,
// the implementation is optimistic and assumes that the weight relies on the number of available actions.
// weightRoles sorts the provided role definitions by the number of LibreGraphWeight,
// descending - false - sorts the roles from least to most permissions
// descending - true - sorts the roles from most to least permissions
func weightRoles(roleSet []*libregraph.UnifiedRoleDefinition, constraints string, descending bool) []*libregraph.UnifiedRoleDefinition {
slices.SortFunc(roleSet, func(i, j *libregraph.UnifiedRoleDefinition) int {
var ia []string
for _, rp := range i.GetRolePermissions() {
if rp.GetCondition() == constraints {
ia = append(ia, rp.GetAllowedResourceActions()...)
}
}
var ja []string
for _, rp := range j.GetRolePermissions() {
if rp.GetCondition() == constraints {
ja = append(ja, rp.GetAllowedResourceActions()...)
}
}
switch descending {
case true:
return cmp.Compare(len(ja), len(ia))
default:
return cmp.Compare(len(ia), len(ja))
slices.SortFunc(roleSet, func(a, b *libregraph.UnifiedRoleDefinition) int {
if descending {
return cmp.Compare(b.GetLibreGraphWeight(), a.GetLibreGraphWeight())
}
return cmp.Compare(a.GetLibreGraphWeight(), b.GetLibreGraphWeight())
})
for i, role := range roleSet {
role.LibreGraphWeight = libregraph.PtrInt32(int32(i) + 1)
}
// return for the sake of consistency, optional because the slice is modified in place
return roleSet
}

View File

@@ -100,16 +100,16 @@ func TestGetRolesByPermissions(t *testing.T) {
givenActions: getRoleActions(unifiedrole.RoleViewer),
constraints: unifiedrole.UnifiedRoleConditionFolder,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
},
},
"RoleViewer | file": {
givenActions: getRoleActions(unifiedrole.RoleViewer),
constraints: unifiedrole.UnifiedRoleConditionFile,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
},
},
"RoleViewer | file | federated": {
@@ -124,8 +124,8 @@ func TestGetRolesByPermissions(t *testing.T) {
givenActions: getRoleActions(unifiedrole.RoleFileEditor),
constraints: unifiedrole.UnifiedRoleConditionFile,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
unifiedrole.RoleFileEditor,
},
},
@@ -133,8 +133,8 @@ func TestGetRolesByPermissions(t *testing.T) {
givenActions: getRoleActions(unifiedrole.RoleEditor),
constraints: unifiedrole.UnifiedRoleConditionFolder,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
unifiedrole.RoleEditorLite,
unifiedrole.RoleEditor,
},
@@ -161,8 +161,8 @@ func TestGetRolesByPermissions(t *testing.T) {
givenActions: getRoleActions(unifiedrole.BuildInRoles...),
constraints: unifiedrole.UnifiedRoleConditionFile,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewerListGrants,
unifiedrole.RoleFileEditor,
unifiedrole.RoleFileEditorListGrants,
@@ -172,13 +172,13 @@ func TestGetRolesByPermissions(t *testing.T) {
givenActions: getRoleActions(unifiedrole.BuildInRoles...),
constraints: unifiedrole.UnifiedRoleConditionFolder,
unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{
unifiedrole.RoleDenied,
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewer,
unifiedrole.RoleSecureViewer,
unifiedrole.RoleViewerListGrants,
unifiedrole.RoleEditorLite,
unifiedrole.RoleEditor,
unifiedrole.RoleEditorListGrants,
unifiedrole.RoleDenied,
},
},
"BuildInRoles | drive": {
@@ -215,7 +215,7 @@ func TestGetRolesByPermissions(t *testing.T) {
for i, generatedDefinition := range generatedDefinitions {
g.Expect(generatedDefinition.Id).To(Equal(tc.unifiedRoleDefinition[i].Id))
g.Expect(*generatedDefinition.LibreGraphWeight).To(Equal(int32(i + 1)))
g.Expect(generatedDefinition.LibreGraphWeight).To(Equal(tc.unifiedRoleDefinition[i].LibreGraphWeight))
}
generatedActions := getRoleActions(generatedDefinitions...)

View File

@@ -85,9 +85,9 @@
"@types/redux-logger": "^3.0.13",
"axios": "^1.7.7",
"classnames": "^2.5.1",
"i18next": "^23.16.8",
"i18next": "^25.1.2",
"i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.5.2",
"i18next-http-backend": "^3.0.2",
"i18next-resources-to-backend": "^1.2.1",
"kpop": "https://download.kopano.io/community/kapp:/kpop-2.7.2.tgz",
"query-string": "^9.1.1",
@@ -117,13 +117,13 @@
"css-loader": "7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"dotenv": "16.4.7",
"dotenv-expand": "10.0.0",
"dotenv-expand": "12.0.2",
"eslint": "^7.32.0",
"eslint-config-react-app": "^6.0.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-flowtype": "^5.10.0",
"eslint-plugin-i18next": "^6.1.1",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^24.7.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.2",

View File

@@ -58,7 +58,7 @@ func DefaultConfig() *config.Config {
IdentifierScopesConf: "",
IdentifierDefaultBannerLogo: "",
IdentifierDefaultSignInPageText: "",
IdentifierDefaultLogoTargetURI: "",
IdentifierDefaultLogoTargetURI: "https://opencloud.eu",
IdentifierDefaultUsernameHintText: "",
SigningKid: "private-key",
SigningMethod: "PS256",

View File

@@ -54,14 +54,14 @@ importers:
specifier: ^2.5.1
version: 2.5.1
i18next:
specifier: ^23.16.8
version: 23.16.8
specifier: ^25.1.2
version: 25.1.2(typescript@5.8.3)
i18next-browser-languagedetector:
specifier: ^7.2.1
version: 7.2.1
i18next-http-backend:
specifier: ^2.5.2
version: 2.5.2(encoding@0.1.13)
specifier: ^3.0.2
version: 3.0.2(encoding@0.1.13)
i18next-resources-to-backend:
specifier: ^1.2.1
version: 1.2.1
@@ -82,7 +82,7 @@ importers:
version: 17.0.2(react@17.0.2)
react-i18next:
specifier: ^15.5.1
version: 15.5.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3)
version: 15.5.1(i18next@25.1.2(typescript@5.8.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3)
react-redux:
specifier: ^8.1.3
version: 8.1.3(@types/react-dom@17.0.25)(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1)
@@ -145,14 +145,14 @@ importers:
specifier: 16.4.7
version: 16.4.7
dotenv-expand:
specifier: 10.0.0
version: 10.0.0
specifier: 12.0.2
version: 12.0.2
eslint:
specifier: ^7.32.0
version: 7.32.0
eslint-config-react-app:
specifier: ^6.0.0
version: 6.0.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(babel-eslint@10.1.0(eslint@7.32.0))(eslint-plugin-flowtype@5.10.0(eslint@7.32.0))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-jest@24.7.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.37.2(eslint@7.32.0))(eslint-plugin-testing-library@3.10.2(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
version: 6.0.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(babel-eslint@10.1.0(eslint@7.32.0))(eslint-plugin-flowtype@5.10.0(eslint@7.32.0))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-jest@24.7.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.37.2(eslint@7.32.0))(eslint-plugin-testing-library@3.10.2(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
eslint-loader:
specifier: ^4.0.2
version: 4.0.2(eslint@7.32.0)(webpack@5.99.6)
@@ -163,8 +163,8 @@ importers:
specifier: ^6.1.1
version: 6.1.1
eslint-plugin-import:
specifier: ^2.30.0
version: 2.30.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
specifier: ^2.31.0
version: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
eslint-plugin-jest:
specifier: ^24.7.0
version: 24.7.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
@@ -279,6 +279,10 @@ packages:
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.26.8':
resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
engines: {node: '>=6.9.0'}
@@ -386,6 +390,10 @@ packages:
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.27.1':
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.25.9':
resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
@@ -984,10 +992,6 @@ packages:
resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.26.7':
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.26.9':
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
engines: {node: '>=6.9.0'}
@@ -996,6 +1000,10 @@ packages:
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.27.1':
resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==}
engines: {node: '>=6.9.0'}
'@babel/template@7.26.9':
resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==}
engines: {node: '>=6.9.0'}
@@ -2094,12 +2102,8 @@ packages:
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
engines: {node: '>= 0.4'}
array.prototype.findlastindex@1.2.5:
resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==}
engines: {node: '>= 0.4'}
array.prototype.flat@1.3.2:
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
array.prototype.findlastindex@1.2.6:
resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
engines: {node: '>= 0.4'}
array.prototype.flat@1.3.3:
@@ -2526,8 +2530,8 @@ packages:
core-js@3.40.0:
resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==}
core-js@3.41.0:
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
core-js@3.42.0:
resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@@ -2854,8 +2858,8 @@ packages:
dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
dotenv-expand@10.0.0:
resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
dotenv-expand@12.0.2:
resolution: {integrity: sha512-lXpXz2ZE1cea1gL4sz2Ipj8y4PiVjytYr3Ij0SWoms1PGxIv7m2CRKuRuCRtHdVuvM/hNJPMxt5PbhboNC4dPQ==}
engines: {node: '>=12'}
dotenv@16.4.7:
@@ -3053,8 +3057,8 @@ packages:
eslint: ^6.0.0 || ^7.0.0
webpack: ^4.0.0 || ^5.0.0
eslint-module-utils@2.11.0:
resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==}
eslint-module-utils@2.12.0:
resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@@ -3084,12 +3088,12 @@ packages:
resolution: {integrity: sha512-/Vy6BfX44njxpRnbJm7bbph0KaNJF2eillqN5W+u03hHuxmh9BjtjdPSrI9HPtyoEbG4j5nBn9gXm/dg99mz3Q==}
engines: {node: '>=0.10.0'}
eslint-plugin-import@2.30.0:
resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==}
eslint-plugin-import@2.31.0:
resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
@@ -3605,8 +3609,8 @@ packages:
engines: {node: '>= 16.0'}
hasBin: true
i18next-http-backend@2.5.2:
resolution: {integrity: sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==}
i18next-http-backend@3.0.2:
resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==}
i18next-parser@9.0.2:
resolution: {integrity: sha512-Q1yTZljBp1DcVAQD7LxduEqFRpjIeZc+5VnQ+gU8qG9WvY3U5rqK0IVONRWNtngh3orb197bfy1Sz4wlwcplxg==}
@@ -3619,6 +3623,14 @@ packages:
i18next@23.16.8:
resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==}
i18next@25.1.2:
resolution: {integrity: sha512-SP63m8LzdjkrAjruH7SCI3ndPSgjt4/wX7ouUUOzCW/eY+HzlIo19IQSfYA9X3qRiRP1SYtaTsg/Oz/PGsfD8w==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
typescript:
optional: true
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@@ -3744,8 +3756,8 @@ packages:
resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==}
engines: {node: '>= 0.4'}
is-core-module@2.15.1:
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
is-data-view@1.0.1:
@@ -5762,9 +5774,6 @@ packages:
resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
engines: {node: '>= 0.4'}
string.prototype.trimend@1.0.8:
resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
string.prototype.trimend@1.0.9:
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
engines: {node: '>= 0.4'}
@@ -6406,6 +6415,12 @@ snapshots:
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/compat-data@7.26.8': {}
'@babel/core@7.26.10':
@@ -6558,6 +6573,8 @@ snapshots:
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-identifier@7.27.1': {}
'@babel/helper-validator-option@7.25.9': {}
'@babel/helper-wrap-function@7.25.9':
@@ -7280,10 +7297,6 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.26.7':
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.26.9':
dependencies:
regenerator-runtime: 0.14.1
@@ -7292,6 +7305,8 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.27.1': {}
'@babel/template@7.26.9':
dependencies:
'@babel/code-frame': 7.26.2
@@ -7669,7 +7684,7 @@ snapshots:
'@eslint/eslintrc@0.4.3':
dependencies:
ajv: 6.12.6
debug: 4.4.0
debug: 4.3.5
espree: 7.3.1
globals: 13.24.0
ignore: 4.0.6
@@ -7691,7 +7706,7 @@ snapshots:
'@humanwhocodes/config-array@0.5.0':
dependencies:
'@humanwhocodes/object-schema': 1.2.1
debug: 4.4.0
debug: 4.3.5
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -8073,8 +8088,8 @@ snapshots:
'@testing-library/dom@10.3.2':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/runtime': 7.27.0
'@babel/code-frame': 7.27.1
'@babel/runtime': 7.27.1
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
@@ -8329,7 +8344,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 4.33.0
'@typescript-eslint/visitor-keys': 4.33.0
debug: 4.4.0
debug: 4.3.5
globby: 11.1.0
is-glob: 4.0.3
semver: 7.7.1
@@ -8513,7 +8528,7 @@ snapshots:
aria-query@4.2.2:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
'@babel/runtime-corejs3': 7.24.8
aria-query@5.3.0:
@@ -8525,7 +8540,7 @@ snapshots:
array-buffer-byte-length@1.0.1:
dependencies:
call-bind: 1.0.8
is-array-buffer: 3.0.4
is-array-buffer: 3.0.5
array-buffer-byte-length@1.0.2:
dependencies:
@@ -8554,21 +8569,15 @@ snapshots:
es-object-atoms: 1.0.0
es-shim-unscopables: 1.0.2
array.prototype.findlastindex@1.2.5:
array.prototype.findlastindex@1.2.6:
dependencies:
call-bind: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.23.3
es-abstract: 1.23.9
es-errors: 1.3.0
es-object-atoms: 1.0.0
es-shim-unscopables: 1.0.2
array.prototype.flat@1.3.2:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.23.3
es-shim-unscopables: 1.0.2
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
array.prototype.flat@1.3.3:
dependencies:
@@ -8601,14 +8610,14 @@ snapshots:
arraybuffer.prototype.slice@1.0.3:
dependencies:
array-buffer-byte-length: 1.0.1
array-buffer-byte-length: 1.0.2
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.9
es-errors: 1.3.0
get-intrinsic: 1.3.0
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
is-array-buffer: 3.0.5
is-shared-array-buffer: 1.0.4
arraybuffer.prototype.slice@1.0.4:
dependencies:
@@ -9103,7 +9112,7 @@ snapshots:
core-js@3.40.0: {}
core-js@3.41.0: {}
core-js@3.42.0: {}
core-util-is@1.0.3: {}
@@ -9227,7 +9236,7 @@ snapshots:
css-vendor@2.0.8:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
is-in-browser: 1.1.3
css-what@6.1.0: {}
@@ -9296,7 +9305,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
is-data-view: 1.0.1
is-data-view: 1.0.2
data-view-buffer@1.0.2:
dependencies:
@@ -9308,7 +9317,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
is-data-view: 1.0.1
is-data-view: 1.0.2
data-view-byte-length@1.0.2:
dependencies:
@@ -9320,7 +9329,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
is-data-view: 1.0.1
is-data-view: 1.0.2
data-view-byte-offset@1.0.1:
dependencies:
@@ -9410,7 +9419,7 @@ snapshots:
dom-helpers@5.2.1:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
csstype: 3.1.3
dom-serializer@1.4.1:
@@ -9452,7 +9461,9 @@ snapshots:
no-case: 3.0.4
tslib: 2.6.3
dotenv-expand@10.0.0: {}
dotenv-expand@12.0.2:
dependencies:
dotenv: 16.4.7
dotenv@16.4.7: {}
@@ -9518,7 +9529,7 @@ snapshots:
array-buffer-byte-length: 1.0.1
arraybuffer.prototype.slice: 1.0.3
available-typed-arrays: 1.0.7
call-bind: 1.0.7
call-bind: 1.0.8
data-view-buffer: 1.0.1
data-view-byte-length: 1.0.1
data-view-byte-offset: 1.0.0
@@ -9553,7 +9564,7 @@ snapshots:
safe-array-concat: 1.1.2
safe-regex-test: 1.1.0
string.prototype.trim: 1.2.9
string.prototype.trimend: 1.0.8
string.prototype.trimend: 1.0.9
string.prototype.trimstart: 1.0.8
typed-array-buffer: 1.0.2
typed-array-byte-length: 1.0.1
@@ -9654,7 +9665,7 @@ snapshots:
es-set-tostringtag@2.0.3:
dependencies:
get-intrinsic: 1.2.4
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
@@ -9732,7 +9743,7 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
eslint-config-react-app@6.0.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(babel-eslint@10.1.0(eslint@7.32.0))(eslint-plugin-flowtype@5.10.0(eslint@7.32.0))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-jest@24.7.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.37.2(eslint@7.32.0))(eslint-plugin-testing-library@3.10.2(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3):
eslint-config-react-app@6.0.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(babel-eslint@10.1.0(eslint@7.32.0))(eslint-plugin-flowtype@5.10.0(eslint@7.32.0))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-jest@24.7.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.37.2(eslint@7.32.0))(eslint-plugin-testing-library@3.10.2(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3):
dependencies:
'@typescript-eslint/eslint-plugin': 4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
'@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.8.3)
@@ -9740,7 +9751,7 @@ snapshots:
confusing-browser-globals: 1.0.11
eslint: 7.32.0
eslint-plugin-flowtype: 5.10.0(eslint@7.32.0)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
eslint-plugin-jsx-a11y: 6.10.2(eslint@7.32.0)
eslint-plugin-react: 7.37.2(eslint@7.32.0)
eslint-plugin-react-hooks: 4.6.2(eslint@7.32.0)
@@ -9752,7 +9763,7 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
is-core-module: 2.15.1
is-core-module: 2.16.1
resolve: 1.22.8
transitivePeerDependencies:
- supports-color
@@ -9767,7 +9778,7 @@ snapshots:
schema-utils: 2.7.1
webpack: 5.99.6
eslint-module-utils@2.11.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0):
eslint-module-utils@2.12.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -9788,26 +9799,27 @@ snapshots:
lodash: 4.17.21
requireindex: 1.1.0
eslint-plugin-import@2.30.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
array.prototype.findlastindex: 1.2.6
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 7.32.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.11.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0)
hasown: 2.0.2
is-core-module: 2.15.1
is-core-module: 2.16.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.0
object.values: 1.2.1
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.8.3)
@@ -10329,7 +10341,7 @@ snapshots:
gopd@1.0.1:
dependencies:
get-intrinsic: 1.2.4
get-intrinsic: 1.3.0
gopd@1.2.0: {}
@@ -10392,7 +10404,7 @@ snapshots:
history@4.10.1:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.3
@@ -10467,7 +10479,7 @@ snapshots:
node-gettext: 3.0.0
p-from-callback: 1.0.1
i18next-http-backend@2.5.2(encoding@0.1.13):
i18next-http-backend@3.0.2(encoding@0.1.13):
dependencies:
cross-fetch: 4.0.0(encoding@0.1.13)
transitivePeerDependencies:
@@ -10501,7 +10513,13 @@ snapshots:
i18next@23.16.8:
dependencies:
'@babel/runtime': 7.26.7
'@babel/runtime': 7.24.8
i18next@25.1.2(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.27.1
optionalDependencies:
typescript: 5.8.3
iconv-lite@0.6.3:
dependencies:
@@ -10599,7 +10617,7 @@ snapshots:
is-bigint@1.0.4:
dependencies:
has-bigints: 1.0.2
has-bigints: 1.1.0
is-bigint@1.1.0:
dependencies:
@@ -10625,13 +10643,13 @@ snapshots:
dependencies:
hasown: 2.0.2
is-core-module@2.15.1:
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
is-data-view@1.0.1:
dependencies:
is-typed-array: 1.1.13
is-typed-array: 1.1.15
is-data-view@1.0.2:
dependencies:
@@ -10744,7 +10762,7 @@ snapshots:
is-typed-array@1.1.13:
dependencies:
which-typed-array: 1.1.15
which-typed-array: 1.1.19
is-typed-array@1.1.15:
dependencies:
@@ -10825,7 +10843,7 @@ snapshots:
iterator.prototype@1.1.3:
dependencies:
define-properties: 1.2.1
get-intrinsic: 1.2.4
get-intrinsic: 1.3.0
has-symbols: 1.0.3
reflect.getprototypeof: 1.0.6
set-function-name: 2.0.2
@@ -11212,46 +11230,46 @@ snapshots:
jss-plugin-camel-case@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
hyphenate-style-name: 1.1.0
jss: 10.10.0
jss-plugin-default-unit@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
jss: 10.10.0
jss-plugin-global@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
jss: 10.10.0
jss-plugin-nested@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
jss: 10.10.0
tiny-warning: 1.0.3
jss-plugin-props-sort@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
jss: 10.10.0
jss-plugin-rule-value-function@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
jss: 10.10.0
tiny-warning: 1.0.3
jss-plugin-vendor-prefixer@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
css-vendor: 2.0.8
jss: 10.10.0
jss@10.10.0:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
csstype: 3.1.3
is-in-browser: 1.1.3
tiny-warning: 1.0.3
@@ -11440,7 +11458,7 @@ snapshots:
mini-create-react-context@0.4.1(prop-types@15.8.1)(react@17.0.2):
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
prop-types: 15.8.1
react: 17.0.2
tiny-warning: 1.0.3
@@ -11517,7 +11535,7 @@ snapshots:
normalize-package-data@5.0.0:
dependencies:
hosted-git-info: 6.1.1
is-core-module: 2.15.1
is-core-module: 2.16.1
semver: 7.7.1
validate-npm-package-license: 3.0.4
@@ -11583,9 +11601,9 @@ snapshots:
object.groupby@1.0.3:
dependencies:
call-bind: 1.0.7
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.23.3
es-abstract: 1.23.9
object.values@1.2.0:
dependencies:
@@ -11604,7 +11622,7 @@ snapshots:
dependencies:
acorn: 7.4.1
base64-js: 1.5.1
core-js: 3.41.0
core-js: 3.42.0
crypto-js: 4.2.0
serialize-javascript: 4.0.0
@@ -12329,11 +12347,11 @@ snapshots:
react-error-overlay@6.0.11: {}
react-i18next@15.5.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3):
react-i18next@15.5.1(i18next@25.1.2(typescript@5.8.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.27.0
html-parse-stringify: 3.0.1
i18next: 23.16.8
i18next: 25.1.2(typescript@5.8.3)
react: 17.0.2
optionalDependencies:
react-dom: 17.0.2(react@17.0.2)
@@ -12383,7 +12401,7 @@ snapshots:
react-router@5.2.1(react@17.0.2):
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
@@ -12525,7 +12543,7 @@ snapshots:
regenerator-transform@0.15.2:
dependencies:
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
regex-parser@2.3.0: {}
@@ -12616,7 +12634,7 @@ snapshots:
resolve@2.0.0-next.5:
dependencies:
is-core-module: 2.15.1
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
@@ -12649,7 +12667,7 @@ snapshots:
safe-array-concat@1.1.2:
dependencies:
call-bind: 1.0.8
get-intrinsic: 1.2.4
get-intrinsic: 1.3.0
has-symbols: 1.0.3
isarray: 2.0.5
@@ -12796,7 +12814,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
get-intrinsic: 1.2.4
get-intrinsic: 1.3.0
object-inspect: 1.13.2
side-channel@1.1.0:
@@ -12982,12 +13000,6 @@ snapshots:
es-abstract: 1.23.9
es-object-atoms: 1.1.1
string.prototype.trimend@1.0.8:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-object-atoms: 1.1.1
string.prototype.trimend@1.0.9:
dependencies:
call-bind: 1.0.8
@@ -13207,7 +13219,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
is-typed-array: 1.1.13
is-typed-array: 1.1.15
typed-array-buffer@1.0.3:
dependencies:
@@ -13219,9 +13231,9 @@ snapshots:
dependencies:
call-bind: 1.0.8
for-each: 0.3.3
gopd: 1.0.1
has-proto: 1.0.3
is-typed-array: 1.1.13
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
typed-array-byte-length@1.0.3:
dependencies:
@@ -13236,9 +13248,9 @@ snapshots:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
for-each: 0.3.3
gopd: 1.0.1
has-proto: 1.0.3
is-typed-array: 1.1.13
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
typed-array-byte-offset@1.0.4:
dependencies:
@@ -13254,9 +13266,9 @@ snapshots:
dependencies:
call-bind: 1.0.8
for-each: 0.3.3
gopd: 1.0.1
has-proto: 1.0.3
is-typed-array: 1.1.13
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
possible-typed-array-names: 1.0.0
typed-array-length@1.0.7:
@@ -13283,7 +13295,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
has-bigints: 1.0.2
has-symbols: 1.0.3
has-symbols: 1.1.0
which-boxed-primitive: 1.0.2
unbox-primitive@1.1.0:
@@ -13570,7 +13582,7 @@ snapshots:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
for-each: 0.3.3
gopd: 1.0.1
gopd: 1.2.0
has-tostringtag: 1.0.2
which-typed-array@1.1.19:
@@ -13607,7 +13619,7 @@ snapshots:
'@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
'@babel/core': 7.26.10
'@babel/preset-env': 7.26.9(@babel/core@7.26.10)
'@babel/runtime': 7.27.0
'@babel/runtime': 7.27.1
'@rollup/plugin-babel': 5.3.1(@babel/core@7.26.10)(@types/babel__core@7.20.5)(rollup@2.79.2)
'@rollup/plugin-node-resolve': 15.3.0(rollup@2.79.2)
'@rollup/plugin-replace': 2.4.2(rollup@2.79.2)

View File

@@ -78,7 +78,7 @@ func Server(cfg *config.Config) *cli.Command {
}
natsServer, err := nats.NewNATSServer(
ctx,
logging.NewLogWrapper(logger),
logger,
nats.Host(cfg.Nats.Host),
nats.Port(cfg.Nats.Port),
nats.ClusterID(cfg.Nats.ClusterID),

View File

@@ -5,6 +5,9 @@ import (
"time"
nserver "github.com/nats-io/nats-server/v2/server"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/nats/pkg/logging"
"github.com/rs/zerolog"
)
var NATSListenAndServeLoopTimer = 1 * time.Second
@@ -15,7 +18,7 @@ type NATSServer struct {
}
// NatsOption configures the new NATSServer instance
func NewNATSServer(ctx context.Context, logger nserver.Logger, opts ...NatsOption) (*NATSServer, error) {
func NewNATSServer(ctx context.Context, logger log.Logger, opts ...NatsOption) (*NATSServer, error) {
natsOpts := &nserver.Options{}
for _, o := range opts {
@@ -32,7 +35,8 @@ func NewNATSServer(ctx context.Context, logger nserver.Logger, opts ...NatsOptio
return nil, err
}
server.SetLoggerV2(logger, true, true, false)
nLogger := logging.NewLogWrapper(logger)
server.SetLoggerV2(nLogger, logger.GetLevel() <= zerolog.DebugLevel, logger.GetLevel() <= zerolog.TraceLevel, false)
return &NATSServer{
ctx: ctx,

View File

@@ -294,6 +294,7 @@ func loadMiddlewares(logger log.Logger, cfg *config.Config,
authenticators = append(authenticators, middleware.AppAuthAuthenticator{
Logger: logger,
RevaGatewaySelector: gatewaySelector,
UserRoleAssigner: roleAssigner,
})
}
authenticators = append(authenticators, middleware.NewOIDCAuthenticator(

View File

@@ -64,9 +64,12 @@ type Route struct {
// Backend is a static URL to forward the request to
Backend string `yaml:"backend,omitempty"`
// Service name to look up in the registry
Service string `yaml:"service,omitempty"`
ApacheVHost bool `yaml:"apache_vhost,omitempty"`
Unprotected bool `yaml:"unprotected,omitempty"`
Service string `yaml:"service,omitempty"`
ApacheVHost bool `yaml:"apache_vhost,omitempty"`
Unprotected bool `yaml:"unprotected,omitempty"`
AdditionalHeaders map[string]string `yaml:"additional_headers,omitempty"`
RemoteUserHeader string `yaml:"remote_user_header,omitempty"`
SkipXAccessToken bool `yaml:"skip_x_access_token"`
}
// RouteType defines the type of route

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/jellydator/ttlcache/v3"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/router"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles"
@@ -99,8 +100,7 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
claims := oidc.FromContext(ctx)
user, ok := revactx.ContextGetUser(ctx)
token := ""
// TODO what if an X-Access-Token is set? happens eg for download requests to the /data endpoint in the reva frontend
token, hasToken := revactx.ContextGetToken(ctx)
if claims == nil && !ok {
m.next.ServeHTTP(w, req)
@@ -192,10 +192,11 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req = req.WithContext(ctx)
m.logger.Debug().Interface("claims", claims).Interface("user", user).Msg("associated claims with user")
} else if user != nil {
} else if user != nil && !hasToken {
// If we already have a token (e.g. the app auth middleware adds the token to the context) there is no need
// to get yet another one here.
var err error
_, token, err = m.userProvider.GetUserByClaims(req.Context(), "username", user.Username)
if errors.Is(err, backend.ErrAccountDisabled) {
m.logger.Debug().Interface("user", user).Msg("Disabled")
w.WriteHeader(http.StatusUnauthorized)
@@ -209,7 +210,13 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
req.Header.Set(revactx.TokenHeader, token)
ri := router.ContextRoutingInfo(ctx)
if ri.RemoteUserHeader() != "" {
req.Header.Set(ri.RemoteUserHeader(), user.GetId().GetOpaqueId())
}
if !ri.SkipXAccessToken() {
req.Header.Set(revactx.TokenHeader, token)
}
m.next.ServeHTTP(w, req)
}

View File

@@ -9,6 +9,7 @@ import (
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/oidc"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/router"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend/mocks"
userRoleMocks "github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles/mocks"
@@ -206,6 +207,7 @@ func mockRequest(claims map[string]interface{}) (*http.Request, *httptest.Respon
}
ctx := oidc.NewContext(context.Background(), claims)
ctx = router.SetRoutingInfo(ctx, router.RoutingInfo{})
req := httptest.NewRequest("GET", "http://example.com/foo", nil).WithContext(ctx)
rw := httptest.NewRecorder()

View File

@@ -6,6 +6,7 @@ import (
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
)
@@ -14,6 +15,7 @@ import (
type AppAuthAuthenticator struct {
Logger log.Logger
RevaGatewaySelector pool.Selectable[gateway.GatewayAPIClient]
UserRoleAssigner userroles.UserRoleAssigner
}
// Authenticate implements the authenticator interface to authenticate requests via app auth.
@@ -43,11 +45,20 @@ func (m AppAuthAuthenticator) Authenticate(r *http.Request) (*http.Request, bool
return nil, false
}
if authenticateResponse.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
// TODO: log???
m.Logger.Debug().Str("msg", authenticateResponse.GetStatus().GetMessage()).Str("clientid", username).Msg("app auth failed")
return nil, false
}
r.Header.Set(revactx.TokenHeader, authenticateResponse.GetToken())
user := authenticateResponse.GetUser()
if user, err = m.UserRoleAssigner.ApplyUserRole(r.Context(), user); err != nil {
m.Logger.Error().Err(err).Str("clientid", username).Msg("app auth: failed to load user roles")
return nil, false
}
ctx := revactx.ContextSetUser(r.Context(), user)
ctx = revactx.ContextSetToken(ctx, authenticateResponse.GetToken())
r = r.WithContext(ctx)
return r, true
}

View File

@@ -5,19 +5,24 @@ import (
"net/http/httptest"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"google.golang.org/grpc"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/opencloud-eu/opencloud/pkg/log"
userRoleMocks "github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles/mocks"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/stretchr/testify/mock"
"google.golang.org/grpc"
)
var _ = Describe("Authenticating requests", Label("AppAuthAuthenticator"), func() {
var authenticator Authenticator
BeforeEach(func() {
pool.RemoveSelector("GatewaySelector" + "eu.opencloud.api.gateway")
ra := &userRoleMocks.UserRoleAssigner{}
ra.On("ApplyUserRole", mock.Anything, mock.Anything, mock.Anything).Return(&userv1beta1.User{}, nil)
authenticator = AppAuthAuthenticator{
Logger: log.NewLogger(),
RevaGatewaySelector: pool.GetSelector[gateway.GatewayAPIClient](
@@ -39,6 +44,7 @@ var _ = Describe("Authenticating requests", Label("AppAuthAuthenticator"), func(
}
},
),
UserRoleAssigner: ra,
}
})
@@ -51,7 +57,12 @@ var _ = Describe("Authenticating requests", Label("AppAuthAuthenticator"), func(
Expect(valid).To(Equal(true))
Expect(req2).ToNot(BeNil())
Expect(req2.Header.Get("x-access-token")).To(Equal("reva-token"))
user, ok := revactx.ContextGetUser(req2.Context())
Expect(ok).To(BeTrue())
Expect(user).ToNot(BeNil())
token, ok := revactx.ContextGetToken(req2.Context())
Expect(ok).To(BeTrue())
Expect(token).To(Equal("reva-token"))
})
})

View File

@@ -163,7 +163,7 @@ var _ = Describe("Authenticating requests", Label("Authentication"), func() {
EnableBasicAuth(true),
)
testHandler := handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expect(r.Header.Get(_headerRevaAccessToken)).To(Equal("otherexampletoken"))
Expect(r.Header.Get(headerRevaAccessToken)).To(Equal("otherexampletoken"))
}))
rr := httptest.NewRecorder()
testHandler.ServeHTTP(rr, req)
@@ -178,7 +178,7 @@ var _ = Describe("Authenticating requests", Label("Authentication"), func() {
EnableBasicAuth(true),
)
testHandler := handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expect(r.Header.Get(_headerRevaAccessToken)).To(Equal("exampletoken"))
Expect(r.Header.Get(headerRevaAccessToken)).To(Equal("exampletoken"))
}))
rr := httptest.NewRecorder()
testHandler.ServeHTTP(rr, req)
@@ -193,7 +193,7 @@ var _ = Describe("Authenticating requests", Label("Authentication"), func() {
EnableBasicAuth(true),
)
testHandler := handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expect(r.Header.Get(_headerRevaAccessToken)).To(Equal("otherexampletoken"))
Expect(r.Header.Get(headerRevaAccessToken)).To(Equal("otherexampletoken"))
}))
rr := httptest.NewRecorder()
testHandler.ServeHTTP(rr, req)

View File

@@ -45,7 +45,7 @@ func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
token := req.Header.Get("x-access-token")
token := req.Header.Get(revactx.TokenHeader)
// we need to pass the token to authenticate the CreateHome request.
//ctx := tokenpkg.ContextSetToken(r.Context(), token)
@@ -84,7 +84,7 @@ func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
func (m createHome) shouldServe(req *http.Request) bool {
return req.Header.Get("x-access-token") != ""
return req.Header.Get(revactx.TokenHeader) != ""
}
func (m createHome) getUserRoles(user *userv1beta1.User) ([]string, error) {

View File

@@ -6,11 +6,12 @@ import (
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/opencloud-eu/opencloud/pkg/log"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
)
const (
_headerRevaAccessToken = "x-access-token"
headerRevaAccessToken = revactx.TokenHeader
headerShareToken = "public-token"
basicAuthPasswordPrefix = "password|"
authenticationType = "publicshares"
@@ -118,7 +119,7 @@ func (a PublicShareAuthenticator) Authenticate(r *http.Request) (*http.Request,
return nil, false
}
r.Header.Add(_headerRevaAccessToken, authResp.Token)
r.Header.Add(headerRevaAccessToken, authResp.Token)
a.Logger.Debug().
Str("authenticator", "public_share").

View File

@@ -58,7 +58,7 @@ var _ = Describe("Authenticating requests", Label("PublicShareAuthenticator"), f
Expect(req2).ToNot(BeNil())
h := req2.Header
Expect(h.Get(_headerRevaAccessToken)).To(Equal("exampletoken"))
Expect(h.Get(headerRevaAccessToken)).To(Equal("exampletoken"))
})
})
Context("using signature authentication", func() {
@@ -71,7 +71,7 @@ var _ = Describe("Authenticating requests", Label("PublicShareAuthenticator"), f
Expect(req2).ToNot(BeNil())
h := req2.Header
Expect(h.Get(_headerRevaAccessToken)).To(Equal("exampletoken"))
Expect(h.Get(headerRevaAccessToken)).To(Equal("exampletoken"))
})
})
})
@@ -85,7 +85,7 @@ var _ = Describe("Authenticating requests", Label("PublicShareAuthenticator"), f
Expect(req2).ToNot(BeNil())
h := req2.Header
Expect(h.Get(_headerRevaAccessToken)).To(Equal("otherexampletoken"))
Expect(h.Get(headerRevaAccessToken)).To(Equal("otherexampletoken"))
})
})
Context("not using a public-token", func() {

View File

@@ -86,9 +86,11 @@ func New(serviceSelector selector.Selector, policySelectorCfg *config.PolicySele
// RoutingInfo contains the proxy rewrite hook and some information about the route.
type RoutingInfo struct {
rewrite func(*httputil.ProxyRequest)
endpoint string
unprotected bool
rewrite func(*httputil.ProxyRequest)
endpoint string
unprotected bool
remoteUserHeader string
skipXAccessToken bool
}
// Rewrite returns the proxy rewrite hook.
@@ -101,6 +103,17 @@ func (r RoutingInfo) IsRouteUnprotected() bool {
return r.unprotected
}
// RemoteUserHeader returns the name of Header for setting the remote user value
func (r RoutingInfo) RemoteUserHeader() string {
return r.remoteUserHeader
}
// SkipXAccessToken return true if the reva access token should not be added to the
// outgoing request
func (r RoutingInfo) SkipXAccessToken() bool {
return r.skipXAccessToken
}
// Router handles the routing of HTTP requests according to the given policies.
type Router struct {
logger log.Logger
@@ -126,8 +139,10 @@ func (rt Router) addHost(policy string, target *url.URL, route config.Route) {
}
rt.rewriters[policy][routeType][route.Method] = append(rt.rewriters[policy][routeType][route.Method], RoutingInfo{
endpoint: route.Endpoint,
unprotected: route.Unprotected,
endpoint: route.Endpoint,
unprotected: route.Unprotected,
remoteUserHeader: route.RemoteUserHeader,
skipXAccessToken: route.SkipXAccessToken,
rewrite: func(req *httputil.ProxyRequest) {
if route.Service != "" {
// select next node
@@ -161,6 +176,10 @@ func (rt Router) addHost(policy string, target *url.URL, route config.Route) {
req.Out.Host = target.Host
}
for k, v := range route.AdditionalHeaders {
req.Out.Header.Set(k, v)
}
req.Out.URL.Path = singleJoiningSlash(target.Path, req.Out.URL.Path)
if targetQuery == "" || req.Out.URL.RawQuery == "" {
req.Out.URL.RawQuery = targetQuery + req.Out.URL.RawQuery
@@ -209,6 +228,8 @@ func (rt Router) Route(r *http.Request) (RoutingInfo, bool) {
if rt.rewriters[pol][rtype][r.Method] != nil {
// use specific method
method = r.Method
} else {
method = ""
}
for _, ri := range rt.rewriters[pol][rtype][method] {

View File

@@ -20,9 +20,7 @@
namespace TestHelpers;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\OcConfigHelper;

View File

@@ -34,6 +34,26 @@ class CliContext implements Context {
private FeatureContext $featureContext;
private SpacesContext $spacesContext;
/**
* opencloud users storage path
*
* @return string
*/
public static function getUsersStoragePath(): string {
$path = getenv('OC_STORAGE_PATH') ?: '/var/lib/opencloud/storage/users';
return $path . '/users';
}
/**
* opencloud project spaces storage path
*
* @return string
*/
public static function getProjectsStoragePath(): string {
$path = getenv('OC_STORAGE_PATH') ?: '/var/lib/opencloud/storage/users';
return $path . '/projects';
}
/**
* @BeforeScenario
*
@@ -85,8 +105,8 @@ class CliContext implements Context {
): void {
$command = "idm resetpassword -u $user";
$body = [
"command" => $command,
"inputs" => [$password, $password]
"command" => $command,
"inputs" => [$password, $password]
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
@@ -105,7 +125,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot();
$command = "trash purge-empty-dirs -p $path --dry-run=false";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -119,7 +139,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot();
$command = "backup consistency -p $path";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -139,7 +159,7 @@ class CliContext implements Context {
$user = $this->featureContext->getActualUserName($user);
$command = "auth-app create --user-name=$user --expiration=$expirationTime";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -159,7 +179,7 @@ class CliContext implements Context {
$user = $this->featureContext->getActualUserName($user);
$command = "auth-app create --user-name=$user --expiration=$expirationTime";
$body = [
"command" => $command
"command" => $command
];
$response = CliHelper::runCommand($body);
@@ -182,7 +202,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot();
$command = "revisions purge -p $path --dry-run=false";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -201,7 +221,7 @@ class CliContext implements Context {
$fileId = $this->spacesContext->getFileId($user, $space, $file);
$command = "revisions purge -p $path -r $fileId --dry-run=false";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -214,7 +234,7 @@ class CliContext implements Context {
public function theAdministratorReindexesAllSpacesUsingTheCli(): void {
$command = "search index --all-spaces";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -230,7 +250,7 @@ class CliContext implements Context {
$spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $spaceName);
$command = "search index --space $spaceId";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -248,7 +268,7 @@ class CliContext implements Context {
$spaceId = $this->spacesContext->getSpaceIdByName($adminUsername, $space);
$command = "revisions purge -p $path -r $spaceId --dry-run=false";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -306,7 +326,7 @@ class CliContext implements Context {
}
$command = "storage-users uploads sessions --json $flag";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -326,7 +346,7 @@ class CliContext implements Context {
$flagString = trim($flag);
$command = "storage-users uploads sessions $flagString --clean --json";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -339,7 +359,7 @@ class CliContext implements Context {
public function theAdministratorRestartsTheUploadSessionsThatAreInPostprocessing(): void {
$command = "storage-users uploads sessions --processing --restart --json";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -365,7 +385,7 @@ class CliContext implements Context {
$command = "storage-users uploads sessions --id=$uploadId --restart --json";
$body = [
"command" => $command
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
@@ -431,9 +451,241 @@ class CliContext implements Context {
public function cleanUploadsSessions(): void {
$command = "storage-users uploads sessions --clean";
$body = [
"command" => $command
"command" => $command
];
$response = CliHelper::runCommand($body);
Assert::assertEquals("200", $response->getStatusCode(), "Failed to clean upload sessions");
}
/**
* @When the administrator creates the folder :folder for user :user on the POSIX filesystem
*
* @param string $folder
* @param string $user
*
* @return void
*/
public function theAdministratorCreatesFolder(string $folder, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$body = [
"command" => "mkdir -p $storagePath/$userUuid/$folder",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator lists the content of the POSIX storage folder of user :user
*
* @param string $user
*
* @return void
*/
public function theAdministratorCheckUsersFolder(string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$body = [
"command" => "ls -la $storagePath/$userUuid",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator creates the file :file with content :content for user :user on the POSIX filesystem
*
* @param string $file
* @param string $content
* @param string $user
*
* @return void
*/
public function theAdministratorCreatesFile(string $file, string $content, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$safeContent = escapeshellarg($content);
$body = [
"command" => "echo -n $safeContent > $storagePath/$userUuid/$file",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator puts the content :content into the file :file in the POSIX storage folder of user :user
*
* @param string $content
* @param string $file
* @param string $user
*
* @return void
*/
public function theAdministratorChangesFileContent(string $content, string $file, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$safeContent = escapeshellarg($content);
$body = [
"command" => "echo -n $safeContent >> $storagePath/$userUuid/$file",
"raw" => true
];
sleep(1);
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator reads the content of the file :file in the POSIX storage folder of user :user
*
* @param string $user
* @param string $file
*
* @return void
*/
public function theAdministratorReadsTheFileContent(string $user, string $file): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$body = [
"command" => "cat $storagePath/$userUuid/$file",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator copies the file :file to the folder :folder for user :user on the POSIX filesystem
*
* @param string $user
* @param string $file
* @param string $folder
*
* @return void
*/
public function theAdministratorCopiesFileToFolder(string $user, string $file, string $folder): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$source = "$storagePath/$userUuid/$file";
$destination = "$storagePath/$userUuid/$folder";
$body = [
"command" => "cp $source $destination",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator moves the file :file to the folder :folder for user :user on the POSIX filesystem
*
* @param string $user
* @param string $file
* @param string $folder
*
* @return void
*/
public function theAdministratorMovesFileToFolder(string $user, string $file, string $folder): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$source = "$storagePath/$userUuid/$file";
$destination = "$storagePath/$userUuid/$folder";
$body = [
"command" => "mv $source $destination",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator deletes the file :file for user :user on the POSIX filesystem
*
* @param string $file
* @param string $user
*
* @return void
*/
public function theAdministratorDeletesFile(string $file, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$body = [
"command" => "rm $storagePath/$userUuid/$file",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator deletes the folder :folder for user :user on the POSIX filesystem
*
* @param string $folder
* @param string $user
*
* @return void
*/
public function theAdministratorDeletesFolder(string $folder, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getUsersStoragePath();
$body = [
"command" => "rm -r $storagePath/$userUuid/$folder",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator copies the file :file to the space :space for user :user on the POSIX filesystem
*
* @param string $user
* @param string $file
* @param string $space
*
* @return void
*/
public function theAdministratorCopiesFileToSpace(string $user, string $file, string $space): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$usersStoragePath = $this->getUsersStoragePath();
$projectsStoragePath = $this->getProjectsStoragePath();
$spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $space);
$spaceId = explode('$', $spaceId)[1];
$source = "$usersStoragePath/$userUuid/$file";
$destination = "$projectsStoragePath/$spaceId";
$body = [
"command" => "cp $source $destination",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator deletes the project space :space on the POSIX filesystem
*
* @param string $space
*
* @return void
*/
public function theAdministratorDeletesSpace(string $space): void {
$projectsStoragePath = $this->getProjectsStoragePath();
$spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $space);
$spaceId = explode('$', $spaceId)[1];
$body = [
"command" => "rm -r $projectsStoragePath/$spaceId",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
}

View File

@@ -458,6 +458,15 @@ default:
- TrashbinContext:
- SpacesTUSContext:
collaborativePosix:
paths:
- "%paths.base%/../features/collaborativePosix"
context: *common_ldap_suite_context
contexts:
- FeatureContext: *common_feature_context_params
- CliContext:
- OcConfigContext:
coreApiMain:
paths:
- "%paths.base%/../features/coreApiMain"

View File

@@ -48,8 +48,8 @@ else
SEARCH_EXTRACTOR_TYPE := basic
endif
# default to decomposedfs
STORAGE_DRIVER ?= decomposed
# default to posix
STORAGE_DRIVER ?= posix
ifeq ($(STORAGE_DRIVER),posix)
# posix requires a additional driver config
COMPOSE_FILE := $(COMPOSE_FILE):src/posix.yml

View File

@@ -10,6 +10,7 @@ services:
WITH_WRAPPER: $WITH_WRAPPER
OC_URL: "https://opencloud-server:9200"
STORAGE_USERS_DRIVER: $STORAGE_DRIVER
STORAGE_USERS_POSIX_WATCH_FS: "true"
STORAGE_USERS_DRIVER_LOCAL_ROOT: /srv/app/tmp/opencloud/local/root
STORAGE_USERS_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/users
STORAGE_SYSTEM_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/metadata

View File

@@ -6,6 +6,7 @@ FROM opencloudeu/opencloud:${OC_IMAGE_TAG} AS opencloud
FROM ubuntu:22.04
COPY --from=opencloud /usr/bin/opencloud /usr/bin/opencloud
RUN apt-get update && apt-get install -y inotify-tools
COPY ["./serve-opencloud.sh", "/usr/bin/serve-opencloud"]
RUN chmod +x /usr/bin/serve-opencloud

View File

@@ -6,3 +6,4 @@ services:
STORAGE_USERS_DRIVER: posix
# posix requires a shared cache store
STORAGE_USERS_ID_CACHE_STORE: "nats-js-kv"
STORAGE_USERS_POSIX_WATCH_FS: "true"

View File

@@ -0,0 +1,231 @@
## Scenarios from OpenCloud API tests that are expected to fail with decomposed storage
#### [Downloading the archive of the resource (files | folder) using resource path is not possible](https://github.com/owncloud/ocis/issues/4637)
- [apiArchiver/downloadByPath.feature:25](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L25)
- [apiArchiver/downloadByPath.feature:26](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L26)
- [apiArchiver/downloadByPath.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L43)
- [apiArchiver/downloadByPath.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L44)
- [apiArchiver/downloadByPath.feature:47](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L47)
- [apiArchiver/downloadByPath.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L73)
- [apiArchiver/downloadByPath.feature:171](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L171)
- [apiArchiver/downloadByPath.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiArchiver/downloadByPath.feature#L172)
#### [PATCH request for TUS upload with wrong checksum gives incorrect response](https://github.com/owncloud/ocis/issues/1755)
- [apiSpacesShares/shareUploadTUS.feature:283](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareUploadTUS.feature#L283)
- [apiSpacesShares/shareUploadTUS.feature:303](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareUploadTUS.feature#L303)
- [apiSpacesShares/shareUploadTUS.feature:384](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareUploadTUS.feature#L384)
#### [Settings service user can list other peoples assignments](https://github.com/owncloud/ocis/issues/5032)
- [apiAccountsHashDifficulty/assignRole.feature:27](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAccountsHashDifficulty/assignRole.feature#L27)
- [apiAccountsHashDifficulty/assignRole.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAccountsHashDifficulty/assignRole.feature#L28)
- [apiGraph/getAssignedRole.feature:31](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraph/getAssignedRole.feature#L31)
- [apiGraph/getAssignedRole.feature:32](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraph/getAssignedRole.feature#L32)
- [apiGraph/getAssignedRole.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraph/getAssignedRole.feature#L33)
#### [A User can get information of another user with Graph API](https://github.com/owncloud/ocis/issues/5125)
- [apiGraphUserGroup/getUser.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L84)
- [apiGraphUserGroup/getUser.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L85)
- [apiGraphUserGroup/getUser.feature:86](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L86)
- [apiGraphUserGroup/getUser.feature:628](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L628)
- [apiGraphUserGroup/getUser.feature:629](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L629)
- [apiGraphUserGroup/getUser.feature:630](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L630)
- [apiGraphUserGroup/getUser.feature:645](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L645)
- [apiGraphUserGroup/getUser.feature:646](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L646)
- [apiGraphUserGroup/getUser.feature:647](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getUser.feature#L647)
#### [Normal user can get expanded members information of a group](https://github.com/owncloud/ocis/issues/5604)
- [apiGraphUserGroup/getGroup.feature:399](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L399)
- [apiGraphUserGroup/getGroup.feature:400](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L400)
- [apiGraphUserGroup/getGroup.feature:401](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L401)
- [apiGraphUserGroup/getGroup.feature:460](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L460)
- [apiGraphUserGroup/getGroup.feature:461](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L461)
- [apiGraphUserGroup/getGroup.feature:462](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L462)
- [apiGraphUserGroup/getGroup.feature:508](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L508)
- [apiGraphUserGroup/getGroup.feature:509](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L509)
- [apiGraphUserGroup/getGroup.feature:510](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/getGroup.feature#L510)
#### [Same users can be added in a group multiple time](https://github.com/owncloud/ocis/issues/5702)
- [apiGraphUserGroup/addUserToGroup.feature:295](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/addUserToGroup.feature#L295)
#### [Users are added in a group with wrong host in host-part of user](https://github.com/owncloud/ocis/issues/5871)
- [apiGraphUserGroup/addUserToGroup.feature:379](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/addUserToGroup.feature#L379)
- [apiGraphUserGroup/addUserToGroup.feature:393](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/addUserToGroup.feature#L393)
#### [Adding the same user as multiple members in a single request results in listing the same user twice in the group](https://github.com/owncloud/ocis/issues/5855)
- [apiGraphUserGroup/addUserToGroup.feature:430](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiGraphUserGroup/addUserToGroup.feature#L430)
#### [Shared file locking is not possible using different path](https://github.com/owncloud/ocis/issues/7599)
- [apiLocks/lockFiles.feature:185](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L185)
- [apiLocks/lockFiles.feature:186](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L186)
- [apiLocks/lockFiles.feature:187](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L187)
- [apiLocks/lockFiles.feature:309](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L309)
- [apiLocks/lockFiles.feature:310](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L310)
- [apiLocks/lockFiles.feature:311](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L311)
- [apiLocks/lockFiles.feature:364](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L364)
- [apiLocks/lockFiles.feature:365](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L365)
- [apiLocks/lockFiles.feature:366](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L366)
- [apiLocks/lockFiles.feature:367](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L367)
- [apiLocks/lockFiles.feature:368](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L368)
- [apiLocks/lockFiles.feature:369](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L369)
- [apiLocks/lockFiles.feature:399](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L399)
- [apiLocks/lockFiles.feature:400](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L400)
- [apiLocks/lockFiles.feature:401](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L401)
- [apiLocks/lockFiles.feature:402](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L402)
- [apiLocks/lockFiles.feature:403](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L403)
- [apiLocks/lockFiles.feature:404](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L404)
- [apiLocks/unlockFiles.feature:62](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L62)
- [apiLocks/unlockFiles.feature:63](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L63)
- [apiLocks/unlockFiles.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L64)
- [apiLocks/unlockFiles.feature:171](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L171)
- [apiLocks/unlockFiles.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L172)
- [apiLocks/unlockFiles.feature:173](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L173)
- [apiLocks/unlockFiles.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L174)
- [apiLocks/unlockFiles.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L175)
- [apiLocks/unlockFiles.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L176)
- [apiLocks/unlockFiles.feature:199](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L199)
- [apiLocks/unlockFiles.feature:200](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L200)
- [apiLocks/unlockFiles.feature:201](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L201)
- [apiLocks/unlockFiles.feature:202](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L202)
- [apiLocks/unlockFiles.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L203)
- [apiLocks/unlockFiles.feature:204](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L204)
- [apiLocks/unlockFiles.feature:227](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L227)
- [apiLocks/unlockFiles.feature:228](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L228)
- [apiLocks/unlockFiles.feature:229](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L229)
- [apiLocks/unlockFiles.feature:230](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L230)
- [apiLocks/unlockFiles.feature:231](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L231)
- [apiLocks/unlockFiles.feature:232](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L232)
#### [Folders can be locked and locking works partially](https://github.com/owncloud/ocis/issues/7641)
- [apiLocks/lockFiles.feature:443](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L443)
- [apiLocks/lockFiles.feature:444](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L444)
- [apiLocks/lockFiles.feature:445](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L445)
- [apiLocks/lockFiles.feature:446](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L446)
- [apiLocks/lockFiles.feature:447](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L447)
- [apiLocks/lockFiles.feature:448](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L448)
- [apiLocks/lockFiles.feature:417](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L417)
- [apiLocks/lockFiles.feature:418](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L418)
- [apiLocks/lockFiles.feature:419](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L419)
- [apiLocks/lockFiles.feature:420](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L420)
- [apiLocks/lockFiles.feature:421](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L421)
- [apiLocks/lockFiles.feature:422](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L422)
#### [Anonymous users can unlock a file shared to them through a public link if they get the lock token](https://github.com/owncloud/ocis/issues/7761)
- [apiLocks/unlockFiles.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L42)
- [apiLocks/unlockFiles.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L43)
- [apiLocks/unlockFiles.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L44)
- [apiLocks/unlockFiles.feature:45](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L45)
- [apiLocks/unlockFiles.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L46)
- [apiLocks/unlockFiles.feature:47](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L47)
#### [Trying to unlock a shared file with sharer's lock token gives 500](https://github.com/owncloud/ocis/issues/7767)
- [apiLocks/unlockFiles.feature:115](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L115)
- [apiLocks/unlockFiles.feature:116](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L116)
- [apiLocks/unlockFiles.feature:117](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L117)
- [apiLocks/unlockFiles.feature:118](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L118)
- [apiLocks/unlockFiles.feature:119](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L119)
- [apiLocks/unlockFiles.feature:120](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L120)
- [apiLocks/unlockFiles.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L143)
- [apiLocks/unlockFiles.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L144)
- [apiLocks/unlockFiles.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L145)
- [apiLocks/unlockFiles.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L146)
- [apiLocks/unlockFiles.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L147)
- [apiLocks/unlockFiles.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L148)
#### [Anonymous user trying lock a file shared to them through a public link gives 405](https://github.com/owncloud/ocis/issues/7790)
- [apiLocks/lockFiles.feature:532](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L532)
- [apiLocks/lockFiles.feature:533](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L533)
- [apiLocks/lockFiles.feature:534](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L534)
- [apiLocks/lockFiles.feature:535](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L535)
- [apiLocks/lockFiles.feature:554](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L554)
- [apiLocks/lockFiles.feature:555](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L555)
- [apiLocks/lockFiles.feature:556](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L556)
- [apiLocks/lockFiles.feature:557](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L557)
#### [sharee (editor role) MOVE a file by file-id into shared sub-folder returns 502](https://github.com/owncloud/ocis/issues/7617)
- [apiSpacesDavOperation/moveByFileId.feature:368](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L368)
- [apiSpacesDavOperation/moveByFileId.feature:591](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L591)
#### [MOVE a file into same folder with same name returns 404 instead of 403](https://github.com/owncloud/ocis/issues/1976)
- [apiSpacesShares/moveSpaces.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/moveSpaces.feature#L69)
- [apiSpacesShares/moveSpaces.feature:70](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/moveSpaces.feature#L70)
- [apiSpacesShares/moveSpaces.feature:416](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/moveSpaces.feature#L416)
- [apiSpacesDavOperation/moveByFileId.feature:61](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L61)
- [apiSpacesDavOperation/moveByFileId.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L174)
- [apiSpacesDavOperation/moveByFileId.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L175)
- [apiSpacesDavOperation/moveByFileId.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L176)
- [apiSpacesDavOperation/moveByFileId.feature:393](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesDavOperation/moveByFileId.feature#L393)
#### [OCM. admin cannot get federated users if he hasn't connection with them ](https://github.com/owncloud/ocis/issues/9829)
- [apiOcm/searchFederationUsers.feature:429](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiOcm/searchFederationUsers.feature#L429)
- [apiOcm/searchFederationUsers.feature:601](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiOcm/searchFederationUsers.feature#L601)
#### [OCM. federated connection is not dropped when one of the users deletes the connection](https://github.com/owncloud/ocis/issues/10216)
- [apiOcm/deleteFederatedConnections.feature:21](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiOcm/deleteFederatedConnections.feature#L21)
- [apiOcm/deleteFederatedConnections.feature:67](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiOcm/deleteFederatedConnections.feature#L67)
#### [OCM. server crash after deleting share for ocm user](https://github.com/owncloud/ocis/issues/10213)
- [apiOcm/deleteFederatedConnections.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiOcm/deleteFederatedConnections.feature#L102)
#### [Shares Jail PROPFIND returns different File IDs for the same item](https://github.com/owncloud/ocis/issues/9933)
- [apiSharingNg1/propfindShares.feature:149](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNg1/propfindShares.feature#L149)
#### [Readiness check for some services returns 500 status code](https://github.com/owncloud/ocis/issues/10661)
- [apiServiceAvailability/serviceAvailabilityCheck.feature:116](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L116)
- [apiServiceAvailability/serviceAvailabilityCheck.feature:125](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L125)
#### [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)
- [apiSearch1/search.feature:438](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L438)
- [apiSearch1/search.feature:439](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L439)
- [apiSearch1/search.feature:465](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L465)
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
#### [No notification triggered for .zip virus file](https://github.com/opencloud-eu/opencloud/issues/382)
- [apiAntivirus/antivirus.feature:41](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L41)
- [apiAntivirus/antivirus.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L43)
- [apiAntivirus/antivirus.feature:45](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L45)
- [apiAntivirus/antivirus.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L69)
- [apiAntivirus/antivirus.feature:71](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L71)
- [apiAntivirus/antivirus.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L73)
- [apiAntivirus/antivirus.feature:115](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L115)
- [apiAntivirus/antivirus.feature:117](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L117)
- [apiAntivirus/antivirus.feature:119](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L119)
- [apiAntivirus/antivirus.feature:141](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L141)
- [apiAntivirus/antivirus.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L143)
- [apiAntivirus/antivirus.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L145)
- [apiAntivirus/antivirus.feature:169](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L169)
- [apiAntivirus/antivirus.feature:171](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L171)
- [apiAntivirus/antivirus.feature:173](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L173)
- [apiAntivirus/antivirus.feature:199](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L199)
- [apiAntivirus/antivirus.feature:201](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L201)
- [apiAntivirus/antivirus.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L203)
- [apiAntivirus/antivirus.feature:228](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L228)
- [apiAntivirus/antivirus.feature:253](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L253)
Note: always have an empty line at the end of this file.
The bash script that processes this file requires that the last line has a newline on the end.

View File

@@ -30,7 +30,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 10
},
"description": {
"const": "View and download."
@@ -152,7 +152,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 40
},
"description": {
"const": "View and download."
@@ -205,7 +205,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 60
},
"description": {
"const": "View, download, upload, edit, add and delete."
@@ -293,7 +293,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -353,7 +353,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 100
},
"description": {
"const": "View, download and edit."
@@ -435,7 +435,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 50
},
"description": {
"const": "View, download and upload."
@@ -488,7 +488,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -564,7 +564,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight":{
"const": 0
"const": 10
},
"description": {
"const": "View and download."
@@ -695,7 +695,7 @@ Feature: permissions role definitions
],
"properties": {
"@libre.graph.weight": {
"const": 0
"const": 20
},
"description": {
"const": "View only documents, images and PDFs. Watermarks will be applied."
@@ -756,4 +756,4 @@ Feature: permissions role definitions
}
}
}
"""
"""

View File

@@ -153,7 +153,7 @@ Feature: List a federated sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -176,7 +176,7 @@ Feature: List a federated sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 100
},
"description": {
"const": "View, download and edit."
@@ -228,7 +228,7 @@ Feature: List a federated sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -251,7 +251,7 @@ Feature: List a federated sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 60
},
"description": {
"const": "View, download, upload, edit, add and delete."

View File

@@ -60,7 +60,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -83,7 +83,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 50
},
"description": {
"const": "View, download and upload."
@@ -106,7 +106,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 60
},
"description": {
"const": "View, download, upload, edit, add and delete."
@@ -183,7 +183,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -206,7 +206,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -229,7 +229,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -316,7 +316,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -339,7 +339,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -362,7 +362,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -580,7 +580,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -603,7 +603,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 100
},
"description": {
"const": "View, download and edit."
@@ -680,7 +680,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -703,7 +703,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 50
},
"description": {
"const": "View, download and upload."
@@ -726,7 +726,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 60
},
"description": {
"const": "View, download, upload, edit, add and delete."
@@ -803,7 +803,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 10
},
"description": {
"const": "View and download."
@@ -826,7 +826,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 100
},
"description": {
"const": "View, download and edit."
@@ -926,7 +926,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -949,7 +949,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -972,7 +972,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -1048,7 +1048,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -1071,7 +1071,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -1094,7 +1094,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -1330,7 +1330,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -1353,7 +1353,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -1376,7 +1376,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -1668,7 +1668,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 40
},
"description": {
"const": "View and download."
@@ -1691,7 +1691,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 90
},
"description": {
"const": "View, download, upload, edit, add, delete including the history."
@@ -1714,7 +1714,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 3
"const": 120
},
"description": {
"const": "View, download, upload, edit, add, delete and manage members."
@@ -2188,7 +2188,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 20
},
"description": {
"const": "View only documents, images and PDFs. Watermarks will be applied."
@@ -2297,7 +2297,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 2
"const": 80
},
"description": {
"const": "View, download, upload, edit, add and delete."
@@ -2377,7 +2377,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 200
},
"description": {
"const": "Deny all access."
@@ -2474,7 +2474,7 @@ Feature: List a sharing permissions
],
"properties": {
"@libre.graph.weight": {
"const": 1
"const": 200
},
"description": {
"const": "Deny all access."

View File

@@ -434,7 +434,7 @@ Feature: Change data of space
When user "<user>" uploads a file inside space "Project Jupiter" with content "" to ".space/newSpaceImage.png" using the WebDAV API
And user "<user>" sets the file ".space/newSpaceImage.png" as a space image in a special section of the "Project Jupiter" space
Then the HTTP status code should be "200"
And the JSON response should contain space called "Project Jupiter" owned by "Alice" with description file ".space/newSpaceImage.png" and match
And the JSON response should contain space called "Project Jupiter" owned by "Alice" with space image ".space/newSpaceImage.png" and match
"""
{
"type": "object",
@@ -597,4 +597,78 @@ Feature: Change data of space
Examples:
| role |
| Space Editor Without Versions |
| Space Editor |
| Space Editor |
@issue-462
Scenario: user doesn't lose the space image when admin fetches the space
Given the administrator has assigned the role "Space Admin" to user "Brian" using the Graph API
And user "Alice" has created a folder ".space" in space "Project Jupiter"
And user "Alice" has uploaded a file inside space "Project Jupiter" with content "" to ".space/spaceImage.jpeg"
And user "Alice" has set the file ".space/spaceImage.jpeg" as a space image in a special section of the "Project Jupiter" space
When user "Brian" lists all spaces via the Graph API
And user "Alice" lists all available spaces via the Graph API with query "$filter=driveType eq 'project'"
Then the HTTP status code should be "200"
And the JSON response should contain space called "Project Jupiter" owned by "Alice" with space image ".space/spaceImage.png" and match
"""
{
"type": "object",
"required": [
"name",
"special"
],
"properties": {
"name": {
"type": "string",
"enum": ["Project Jupiter"]
},
"special": {
"type": "array",
"minItems": 1,
"maxItems": 1,
"items": {
"type": "object",
"required": [
"size",
"name",
"specialFolder",
"file"
],
"properties": {
"size": {
"type": "number",
"enum": [0]
},
"name": {
"type": "string",
"enum": ["spaceImage.jpeg"]
},
"specialFolder": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"enum": ["image"]
}
}
},
"file": {
"type": "object",
"required": [
"mimeType"
],
"properties": {
"mimeType": {
"type": "string",
"enum": ["image/jpeg"]
}
}
}
}
}
}
}
}
"""

View File

@@ -0,0 +1,87 @@
@env-config @skipOnOpencloud-decomposed-Storage @skipOnOpencloud-decomposeds3-Storage
Feature: create a resources using collaborative posixfs
Background:
Given the config "STORAGE_USERS_POSIX_WATCH_FS" has been set to "true"
And user "Alice" has been created with default attributes
Scenario: create folder
Given user "Alice" has uploaded file with content "content" to "textfile.txt"
When the administrator creates the folder "myFolder" for user "Alice" on the POSIX filesystem
Then the command should be successful
When the administrator lists the content of the POSIX storage folder of user "Alice"
Then the command output should contain "myFolder"
And as "Alice" folder "/myFolder" should exist
Scenario: create file
Given user "Alice" has created folder "/folder"
When the administrator creates the file "test.txt" with content "content" for user "Alice" on the POSIX filesystem
Then the command should be successful
When the administrator lists the content of the POSIX storage folder of user "Alice"
Then the command output should contain "test.txt"
And the content of file "/test.txt" for user "Alice" should be "content"
Scenario: edit file
Given user "Alice" has uploaded file with content "content" to "test.txt"
When the administrator puts the content "new" into the file "test.txt" in the POSIX storage folder of user "Alice"
Then the content of file "/test.txt" for user "Alice" should be "contentnew"
Scenario: read file content
Given user "Alice" has uploaded file with content "content" to "textfile.txt"
When the administrator reads the content of the file "textfile.txt" in the POSIX storage folder of user "Alice"
Then the command output should contain "content"
Scenario: copy file to folder
Given user "Alice" has created folder "/folder"
And user "Alice" has uploaded file with content "content" to "test.txt"
When the administrator copies the file "test.txt" to the folder "folder" for user "Alice" on the POSIX filesystem
Then the command should be successful
And the content of file "/folder/test.txt" for user "Alice" should be "content"
Scenario: move file to folder
Given user "Alice" has created folder "/folder"
And user "Alice" has uploaded file with content "content" to "test.txt"
When the administrator moves the file "test.txt" to the folder "folder" for user "Alice" on the POSIX filesystem
Then the command should be successful
And the content of file "/folder/test.txt" for user "Alice" should be "content"
And as "Alice" file "/test.txt" should not exist
Scenario: delete file
Given user "Alice" has uploaded file with content "content" to "test.txt"
When the administrator deletes the file "test.txt" for user "Alice" on the POSIX filesystem
Then the command should be successful
And as "Alice" file "/test.txt" should not exist
Scenario: delete folder
Given user "Alice" has created folder "/folder"
And user "Alice" has uploaded file with content "content" to "/folder/test.txt"
When the administrator deletes the folder "folder" for user "Alice" on the POSIX filesystem
Then the command should be successful
And as "Alice" folder "folder" should not exist
Scenario: copy file from personal to project space
Given user "Alice" has uploaded file with content "content" to "test.txt"
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
And user "Alice" has created a space "Project space" with the default quota using the Graph API
When the administrator copies the file "test.txt" to the space "Project space" for user "Alice" on the POSIX filesystem
Then the command should be successful
And using spaces DAV path
And for user "Alice" the space "Project space" should contain these entries:
| test.txt |
Scenario: delete project space
Given the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
And user "Alice" has created a space "Project space" with the default quota using the Graph API
When the administrator deletes the project space "Project space" on the POSIX filesystem
Then the command should be successful
And the user "Alice" should not have a space called "Project space"

View File

@@ -271,3 +271,36 @@ func RunCommand(command string, inputs []string) (int, string) {
return c.ProcessState.ExitCode(), cmdOutput
}
func RunRawCommand(command string, inputs []string) (int, string) {
logs := new(strings.Builder)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
fmt.Print("Running command: ", command)
c := exec.CommandContext(ctx, "bash", "-c", command)
ptyF, err := pty.Start(c)
if err != nil {
log.Panic(err)
}
defer ptyF.Close()
for _, input := range inputs {
fmt.Fprintf(ptyF, "%s\n", input)
}
var cmdOutput string
if err := c.Wait(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
cmdOutput = "Command timed out:\n"
}
}
io.Copy(logs, ptyF)
cmdOutput += logs.String()
cmdOutput = strings.TrimLeft(cmdOutput, strings.Join(inputs, "\r\n"))
return c.ProcessState.ExitCode(), cmdOutput
}

View File

@@ -198,7 +198,19 @@ func CommandHandler(res http.ResponseWriter, req *http.Request) {
}
}
}
raw := false
if r, ok := body["raw"].(bool); ok {
raw = r
}
exitCode, output := opencloud.RunCommand(command, stdIn)
var exitCode int
var output string
if raw {
exitCode, output = opencloud.RunRawCommand(command, stdIn)
} else {
exitCode, output = opencloud.RunCommand(command, stdIn)
}
sendCmdResponse(res, exitCode, output)
}

7
vendor/dario.cat/mergo/FUNDING.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"drips": {
"ethereum": {
"ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
}
}
}

View File

@@ -85,7 +85,6 @@ Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/depend
* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
* [go-micro/go-micro](https://github.com/go-micro/go-micro)
* [grafana/loki](https://github.com/grafana/loki)
* [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
* [masterminds/sprig](github.com/Masterminds/sprig)
* [moby/moby](https://github.com/moby/moby)
* [slackhq/nebula](https://github.com/slackhq/nebula)
@@ -191,10 +190,6 @@ func main() {
}
```
Note: if test are failing due missing package, please execute:
go get gopkg.in/yaml.v3
### Transformers
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?

View File

@@ -4,8 +4,8 @@
| Version | Supported |
| ------- | ------------------ |
| 0.3.x | :white_check_mark: |
| < 0.3 | :x: |
| 1.x.x | :white_check_mark: |
| < 1.0 | :x: |
## Security contact information

View File

@@ -58,8 +58,9 @@ const (
ExpressionTokenDuration // duration = [ "duration" ] SQUOTE durationValue SQUOTE
ExpressionTokenGuid // [25] A 128-bit GUID
ExpressionTokenAssignement // The '=' assignement for function arguments.
ExpressionTokenGeographyPolygon //
ExpressionTokenGeometryPolygon //
ExpressionTokenGeographyPolygon // A polygon with geodetic (ie spherical) coordinates. Parsed Token.Value is '<long> <lat>,<long> <lat>...'
ExpressionTokenGeometryPolygon // A polygon with planar (ie cartesian) coordinates. Parsed Token.Value is '<long> <lat>,<long> <lat>...'
ExpressionTokenGeographyPoint // A geodetic coordinate point. Parsed Token.Value is '<long> <lat>'
expressionTokenLast
)
@@ -94,6 +95,7 @@ func (e ExpressionTokenType) String() string {
"ExpressionTokenAssignement",
"ExpressionTokenGeographyPolygon",
"ExpressionTokenGeometryPolygon",
"ExpressionTokenGeographyPoint",
"expressionTokenLast",
}[e]
}
@@ -178,15 +180,11 @@ func NewExpressionTokenizer() *Tokenizer {
// E.g. ABNF for 'geo.distance':
// distanceMethodCallExpr = "geo.distance" OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE
t.Add("(?i)^(?P<token>(geo.distance|geo.intersects|geo.length))[\\s(]", ExpressionTokenFunc)
// geographyPolygon = geographyPrefix SQUOTE fullPolygonLiteral SQUOTE
// fullPolygonLiteral = sridLiteral polygonLiteral
// sridLiteral = "SRID" EQ 1*5DIGIT SEMI
// polygonLiteral = "Polygon" polygonData
// polygonData = OPEN ringLiteral *( COMMA ringLiteral ) CLOSE
// Example: geography'SRID=0;Polygon((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581))'
t.Add(`^geography'SRID=[0-9]{1,5};Polygon\(\((-?[0-9]+\.[0-9]+\s+-?[0-9]+\.[0-9]+)(,\s-?[0-9]+\.[0-9]+\s+-?[0-9]+\.[0-9]+)*\)\)'`, ExpressionTokenGeographyPolygon)
// geometryPolygon = geometryPrefix SQUOTE fullPolygonLiteral SQUOTE
t.Add(`^geometry'SRID=[0-9]{1,5};Polygon\(\((-?[0-9]+\.[0-9]+\s+-?[0-9]+\.[0-9]+)(,\s-?[0-9]+\.[0-9]+\s+-?[0-9]+\.[0-9]+)*\)\)'`, ExpressionTokenGeometryPolygon)
// Example: geography'POLYGON((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581))'
t.Add(`(?i)^geography'(?:SRID=(\d{1,5});)?POLYGON\s*\(\(\s*(?P<subtoken>-?\d+(\.\d+)?\s+-?\d+(\.\d+)?(?:\s*,\s*-?\d+(\.\d+)?\s+-?\d+(\.\d+)?)*?)\s*\)\)'`, ExpressionTokenGeographyPolygon)
t.Add(`(?i)^geometry'(?:SRID=(\d{1,5});)?POLYGON\s*\(\(\s*(?P<subtoken>-?\d+(\.\d+)?\s+-?\d+(\.\d+)?(?:\s*,\s*-?\d+(\.\d+)?\s+-?\d+(\.\d+)?)*?)\s*\)\)'`, ExpressionTokenGeometryPolygon)
// Example: geography'POINT(-122.131577 47.678581)'
t.Add(`(?i)^geography'POINT\s*\(\s*(?P<subtoken>-?\d+(\.\d+)?\s+-?\d+(\.\d+)?)\s*\)'`, ExpressionTokenGeographyPoint)
// According to ODATA ABNF notation, functions must be followed by a open parenthesis with no space
// between the function name and the open parenthesis.
// However, we are leniently allowing space characters between the function and the open parenthesis.
@@ -315,7 +313,7 @@ func NewExpressionParser() *ExpressionParser {
// Edm.Boolean geo.intersects(Edm.GeometryPoint,Edm.GeometryPolygon)
// The geo.intersects function returns true if the specified point lies within the interior
// or on the boundary of the specified polygon, otherwise it returns false.
parser.DefineFunction("geo.intersects", []int{2}, false)
parser.DefineFunction("geo.intersects", []int{2}, true)
// The geo.length function has the following signatures:
// Edm.Double geo.length(Edm.GeographyLineString)
// Edm.Double geo.length(Edm.GeometryLineString)
@@ -329,7 +327,7 @@ func NewExpressionParser() *ExpressionParser {
parser.DefineFunction("all", []int{2}, true)
// Define 'case' as a function accepting 1-10 arguments. Each argument is a pair of expressions separated by a colon.
// See https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_case
parser.DefineFunction("case", []int{1,2,3,4,5,6,7,8,9,10}, true)
parser.DefineFunction("case", []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, true)
return parser
}

View File

@@ -21,8 +21,7 @@ func ParseFilterString(ctx context.Context, filter string) (*GoDataFilterQuery,
if err != nil {
return nil, err
}
if tree == nil || tree.Token == nil ||
(len(tree.Children) == 0 && tree.Token.Type != ExpressionTokenBoolean) {
if tree == nil || tree.Token == nil || !GlobalFilterParser.isBooleanExpression(tree.Token) {
return nil, BadRequestError("Value must be a boolean expression")
}
return &GoDataFilterQuery{tree, filter}, nil

View File

@@ -276,11 +276,9 @@ func parseMountInfoLine(line string) (mountInfo, error) {
fields1 = append(fields1, "")
}
fields2 := strings.Split(fieldss[1], " ")
fields2 := strings.SplitN(fieldss[1], " ", 3)
if len(fields2) < 3 {
return mountInfo{}, fmt.Errorf("not enough fields after separator: %v", fields2)
} else if len(fields2) > 3 {
return mountInfo{}, fmt.Errorf("too many fields after separator: %v", fields2)
}
return mountInfo{

View File

@@ -12,3 +12,4 @@ Martin Dosch (mdosch)
Hugo Wetterberg (hugowetterberg)
Tobias Theel (nerzal)
Daniel Potapov (dpotapov)
Mikhail Ferapontow (MikhailFerapontow)

View File

@@ -1,3 +1,10 @@
Release 1.5.1
=============
**Fixes**
* Fixed a bug in `InsertChildAt`.
Release 1.5.0
=============

View File

@@ -831,7 +831,7 @@ func (e *Element) InsertChildAt(index int, t Token) {
}
if t.Parent() != nil {
if t.Parent() == e && t.Index() > index {
if t.Parent() == e && t.Index() < index {
index--
}
t.Parent().RemoveChild(t)

View File

@@ -7,7 +7,6 @@ import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
@@ -16,11 +15,12 @@ import (
"strconv"
"time"
"golang.org/x/xerrors"
"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/parser"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)
// Decoder reads and decodes YAML values from an input stream.
@@ -488,6 +488,21 @@ func (d *Decoder) fileToNode(f *ast.File) ast.Node {
func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) {
if typ.Kind() != reflect.String {
if !v.Type().ConvertibleTo(typ) {
// Special case for "strings -> floats" aka scientific notation
// If the destination type is a float and the source type is a string, check if we can
// use strconv.ParseFloat to convert the string to a float.
if (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) &&
v.Type().Kind() == reflect.String {
if f, err := strconv.ParseFloat(v.String(), 64); err == nil {
if typ.Kind() == reflect.Float32 {
return reflect.ValueOf(float32(f)), nil
} else if typ.Kind() == reflect.Float64 {
return reflect.ValueOf(f), nil
}
// else, fall through to the error below
}
}
return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken())
}
return v.Convert(typ), nil
@@ -877,6 +892,15 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetInt(int64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowInt(int64(i)) {
dst.SetInt(int64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -899,6 +923,16 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetUint(uint64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowUint(uint64(i)) {
dst.SetUint(uint64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -1501,10 +1535,19 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
}
continue
}
k := reflect.ValueOf(d.nodeToValue(key))
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
k := d.createDecodableValue(keyType)
if d.canDecodeByUnmarshaler(k) {
if err := d.decodeByUnmarshaler(ctx, k, key); err != nil {
return errors.Wrapf(err, "failed to decode by unmarshaler")
}
} else {
k = reflect.ValueOf(d.nodeToValue(key))
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
}
}
if k.IsValid() {
if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil {
return errors.Wrapf(err, "invalid map key")
@@ -1621,7 +1664,7 @@ func (d *Decoder) resolveReference() error {
}
}
for _, reader := range d.referenceReaders {
bytes, err := ioutil.ReadAll(reader)
bytes, err := io.ReadAll(reader)
if err != nil {
return errors.Wrapf(err, "failed to read buffer")
}

View File

@@ -2,7 +2,7 @@ package parser
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/goccy/go-yaml/ast"
@@ -730,7 +730,7 @@ func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) {
// Parse parse from filename, and returns ast.File
func ParseFile(filename string, mode Mode) (*ast.File, error) {
file, err := ioutil.ReadFile(filename)
file, err := os.ReadFile(filename)
if err != nil {
return nil, errors.Wrapf(err, "failed to read file: %s", filename)
}

View File

@@ -468,7 +468,7 @@ func (n *rootNode) String() string {
func (n *rootNode) filter(node ast.Node) (ast.Node, error) {
if n.child == nil {
return nil, nil
return node, nil
}
filtered, err := n.child.filter(node)
if err != nil {

View File

@@ -196,9 +196,16 @@ func (c *Context) existsBuffer() bool {
func (c *Context) bufferedSrc() []rune {
src := c.buf[:c.notSpaceCharPos]
if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" {
// remove end '\n' character
src = src[:len(src)-1]
if c.isDocument() && c.literalOpt == "-" {
// remove end '\n' character and trailing empty lines
// https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator
for {
if len(src) > 0 && src[len(src)-1] == '\n' {
src = src[:len(src)-1]
continue
}
break
}
}
return src
}

View File

@@ -4,8 +4,9 @@ import (
"io"
"strings"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
"github.com/goccy/go-yaml/token"
)
// IndentState state for indent
@@ -316,100 +317,93 @@ func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) {
continue
} else if c == '\\' {
isFirstLineChar = false
if idx+1 < size {
nextChar := src[idx+1]
switch nextChar {
case 'b':
ctx.addOriginBuf(nextChar)
value = append(value, '\b')
idx++
continue
case 'e':
ctx.addOriginBuf(nextChar)
value = append(value, '\x1B')
idx++
continue
case 'f':
ctx.addOriginBuf(nextChar)
value = append(value, '\f')
idx++
continue
case 'n':
ctx.addOriginBuf(nextChar)
value = append(value, '\n')
idx++
continue
case 'r':
ctx.addOriginBuf(nextChar)
value = append(value, '\r')
idx++
continue
case 'v':
ctx.addOriginBuf(nextChar)
value = append(value, '\v')
idx++
continue
case 'L': // LS (#x2028)
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA8'}...)
idx++
continue
case 'N': // NEL (#x85)
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\x85'}...)
idx++
continue
case 'P': // PS (#x2029)
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA9'}...)
idx++
continue
case '_': // #xA0
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\xA0'}...)
idx++
continue
case '"':
ctx.addOriginBuf(nextChar)
value = append(value, nextChar)
idx++
continue
case 'x':
if idx+3 >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\x")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+4])
value = append(value, rune(codeNum))
idx += 3
continue
case 'u':
if idx+5 >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\u")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+6])
value = append(value, rune(codeNum))
idx += 5
continue
case 'U':
if idx+9 >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\U")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+10])
value = append(value, rune(codeNum))
idx += 9
continue
case '\\':
ctx.addOriginBuf(nextChar)
idx++
}
if idx+1 >= size {
value = append(value, c)
continue
}
value = append(value, c)
nextChar := src[idx+1]
progress := 0
switch nextChar {
case 'b':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\b')
case 'e':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\x1B')
case 'f':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\f')
case 'n':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\n')
case 'r':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\r')
case 'v':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\v')
case 'L': // LS (#x2028)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA8'}...)
case 'N': // NEL (#x85)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\x85'}...)
case 'P': // PS (#x2029)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA9'}...)
case '_': // #xA0
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\xA0'}...)
case '"':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, nextChar)
case 'x':
progress = 3
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\x")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
case 'u':
progress = 5
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\u")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
case 'U':
progress = 9
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\U")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
case '\\':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, c)
default:
value = append(value, c)
}
idx += progress
s.progressColumn(ctx, progress)
continue
} else if c != '"' {
value = append(value, c)
@@ -621,6 +615,16 @@ func (s *Scanner) scanNewLine(ctx *Context, c rune) {
}
}
// There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec.
// > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character.
// > Outside scalar content, YAML allows any line break to be used to terminate lines.
// > -- https://yaml.org/spec/1.2/spec.html
if c == '\r' && ctx.nextChar() == '\n' {
ctx.addOriginBuf('\r')
ctx.progress(1)
c = '\n'
}
if ctx.isEOS() {
s.addBufferedTokenIfExists(ctx)
} else if s.isAnchor {
@@ -840,15 +844,6 @@ func (s *Scanner) scan(ctx *Context) (pos int) {
return
}
case '\r', '\n':
// There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec.
// > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character.
// > Outside scalar content, YAML allows any line break to be used to terminate lines.
// > -- https://yaml.org/spec/1.2/spec.html
if c == '\r' && ctx.nextChar() == '\n' {
ctx.addOriginBuf('\r')
ctx.progress(1)
c = '\n'
}
s.scanNewLine(ctx, c)
continue
case ' ':

View File

@@ -623,7 +623,7 @@ func IsNeedQuoted(value string) bool {
}
first := value[0]
switch first {
case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ':
case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ', '`':
return true
}
last := value[len(value)-1]

View File

@@ -231,15 +231,57 @@ name = config.String("name")
fmt.Print(name) // "new name"
```
## Load from flags
## Load from ENV
> Support simple flags parameter parsing, loading
Support load ENV vars to config data.
- Support set value to sub key in map.
- eg: `{"DB_USERNAME": "db.username"}` value will set to `username` in `db`
```go
// flags like: --name inhere --env dev --age 99 --debug
// os env: APP_NAME=config APP_DEBUG=true DB_USERNAME=someone
// load ENV info
config.LoadOSEnvs(map[string]string{"APP_NAME": "app_name", "APP_DEBUG": "app_debug", "DB_USERNAME": "db.username"})
// read
config.Bool("app_debug") // true
config.String("app_name") // "config"
```
## Load from flags
Support simple CLI flags parameter parsing, load to config data.
- define format: `name:type:desc` OR `name:type` OR `name:desc` (type, desc is optional)
- `type` can set `flag` type. allow: `bool`, `int`, `string`(default)
- `desc` can set `flag` description
- `name` can be in key path format.
- eg: `db.username`, input: `--db.username=someone` values will be mapped to `username` of the `db` configuration
```go
// 'debug' flag is bool type
config.LoadFlags([]string{"env", "debug:bool"})
// can with flag desc message
config.LoadFlags([]string{"env:set the run env"})
config.LoadFlags([]string{"debug:bool:set debug mode"})
// can set value to map key. eg: myapp --map1.sub-key=val
config.LoadFlags([]string{"map1.sub-key"})
```
Examples:
```go
// flags like: --name inhere --env dev --age 99 --debug --map1.sub-key=val
// load flag info
keys := []string{"name", "env", "age:int" "debug:bool"}
keys := []string{
"name",
"env:set the run env",
"age:int",
"debug:bool:set debug mode",
"map1.sub-key",
}
err := config.LoadFlags(keys)
// read
@@ -247,18 +289,7 @@ config.String("name") // "inhere"
config.String("env") // "dev"
config.Int("age") // 99
config.Bool("debug") // true
```
## Load from ENV
```go
// os env: APP_NAME=config APP_DEBUG=true
// load ENV info
config.LoadOSEnvs(map[string]string{"APP_NAME": "app_name", "APP_DEBUG": "app_debug"})
// read
config.Bool("app_debug") // true
config.String("app_name") // "config"
config.Get("map1") // map[string]any{"sub-key":"val"}
```
## New config instance
@@ -377,6 +408,8 @@ type Options struct {
}
```
> **TIP**: please visit https://pkg.go.dev/github.com/gookit/config/v2#Options to see the latest options information
Examples for set options:
```go

View File

@@ -220,8 +220,19 @@ name = config.String("name")
fmt.Print(name) // new name
```
## 加载配置文件
- `LoadExists(sourceFiles ...string) (err error)` 从存在的配置文件里加载数据,会忽略不存在的文件
- `LoadFiles(sourceFiles ...string) (err error)` 从给定的配置文件里加载数据有文件不存在则会panic
> **TIP**: 更多加载方式请查看 `config.Load*` 相关方法
## 从ENV载入数据
`LoadOSEnvs` 支持从环境变量中读取数据,并解析为配置数据。格式为 `ENV_NAME: config_key`
- `config_key` 可以是 key path 格式。 eg: `{"DB_USERNAME": "db.username"}` 值将会映射到 `db` 配置的 `username`
```go
// os env: APP_NAME=config APP_DEBUG=true
// load ENV info
@@ -234,13 +245,36 @@ config.String("app_name") // "config"
## 从命令行参数载入数据
支持简单的从命令行 `flag` 参数解析,加载数据
支持简单的从命令行 `flag` 参数解析,加载数据
- 配置参数格式为 `name:type:desc` OR `name:type` OR `name:desc` (type, desc 是可选的)
- `type` 可以设置 `flag` 的类型,支持 `bool`, `int`, `string`(默认)
- `desc` 可以设置 `flag` 的描述信息
- `name` 可以是 key path 格式。 eg: `db.username`, input: `--db.username=someone` 值将会映射到 `db` 配置的 `username`
```go
// flags like: --name inhere --env dev --age 99 --debug
// 'debug' flag is bool type
config.LoadFlags([]string{"env", "debug:bool"})
// can with flag desc message
config.LoadFlags([]string{"env:set the run env"})
config.LoadFlags([]string{"debug:bool:set debug mode"})
// can set value to map key. eg: myapp --map1.sub-key=val
config.LoadFlags([]string{"map1.sub-key"})
```
Examples:
```go
// flags like: --name inhere --env dev --age 99 --debug --map1.sub-key=val
// load flag info
keys := []string{"name", "env", "age:int" "debug:bool"}
keys := []string{
"name",
"env:set the run env",
"age:int",
"debug:bool:set debug mode",
"map1.sub-key",
}
err := config.LoadFlags(keys)
// read
@@ -248,6 +282,7 @@ config.String("name") // "inhere"
config.String("env") // "dev"
config.Int("age") // 99
config.Bool("debug") // true
config.Get("map1") // map[string]any{"sub-key":"val"}
```
## 创建自定义实例
@@ -366,6 +401,8 @@ type Options struct {
}
```
> **提示**: 访问 https://pkg.go.dev/github.com/gookit/config/v2#Options 查看最新的选项信息
Examples for set options:
```go
@@ -417,7 +454,7 @@ NEW: 支持通过结构标签 `default` 解析并设置默认值
- `LoadData(dataSource ...any) (err error)` 从struct或map加载数据
- `LoadFlags(keys []string) (err error)` 从命令行参数载入数据
- `LoadOSEnvs(nameToKeyMap map[string]string)` 从ENV载入数据
- `LoadOSEnvs(nameToKeyMap map[string]string)` 从ENV载入配置数据
- `LoadExists(sourceFiles ...string) (err error)` 从存在的配置文件里加载数据,会忽略不存在的文件
- `LoadFiles(sourceFiles ...string) (err error)` 从给定的配置文件里加载数据有文件不存在则会panic
- `LoadFromDir(dirPath, format string) (err error)` 从给定目录里加载自定格式的文件,文件名会作为 key

View File

@@ -116,6 +116,13 @@ func New(name string, opts ...OptionFn) *Config {
return NewEmpty(name, opts...).WithDriver(JSONDriver)
}
// NewGeneric create generic config instance with custom options.
//
// - default add options: ParseEnv, ParseDefault, ParseTime
func NewGeneric(name string, opts ...OptionFn) *Config {
return NewEmpty(name, ParseEnv, ParseDefault, ParseTime).WithOptions(opts...).WithDriver(JSONDriver)
}
// NewEmpty create config instance with custom options
func NewEmpty(name string, opts ...OptionFn) *Config {
c := &Config{

View File

@@ -108,18 +108,19 @@ func (c *Config) LoadOSEnv(keys []string, keyToLower bool) {
c.fireHook(OnLoadData)
}
// LoadOSEnvs load data from OS ENVs. format: {ENV_NAME: config_key}
// LoadOSEnvs load data from OS ENVs. see Config.LoadOSEnvs
func LoadOSEnvs(nameToKeyMap map[string]string) { dc.LoadOSEnvs(nameToKeyMap) }
// LoadOSEnvs load data from os ENVs. format: {ENV_NAME: config_key}
// LoadOSEnvs load data from os ENVs. format: `{ENV_NAME: config_key}`
//
// - `config_key` allow use key path. eg: `{"DB_USERNAME": "db.username"}`
func (c *Config) LoadOSEnvs(nameToKeyMap map[string]string) {
for name, key := range nameToKeyMap {
for name, cfgKey := range nameToKeyMap {
if val := os.Getenv(name); val != "" {
if key == "" {
key = strings.ToLower(name)
if cfgKey == "" {
cfgKey = strings.ToLower(name)
}
_ = c.Set(key, val)
_ = c.Set(cfgKey, val)
}
}
@@ -135,8 +136,8 @@ var validTypes = map[string]int{
"string": 1,
}
// LoadFlags load data from cli flags
func LoadFlags(keys []string) error { return dc.LoadFlags(keys) }
// LoadFlags load data from cli flags. see Config.LoadFlags
func LoadFlags(defines []string) error { return dc.LoadFlags(defines) }
// LoadFlags parse command line arguments, based on provide keys.
//
@@ -144,13 +145,20 @@ func LoadFlags(keys []string) error { return dc.LoadFlags(keys) }
//
// // 'debug' flag is bool type
// c.LoadFlags([]string{"env", "debug:bool"})
func (c *Config) LoadFlags(keys []string) (err error) {
// // can with flag desc message
// c.LoadFlags([]string{"env:set the run env"})
// c.LoadFlags([]string{"debug:bool:set debug mode"})
// // can set value to map key. eg: myapp --map1.sub-key=val
// c.LoadFlags([]string{"--map1.sub-key"})
func (c *Config) LoadFlags(defines []string) (err error) {
hash := map[string]int8{}
// bind vars
for _, key := range keys {
key, typ := parseVarNameAndType(key)
desc := "config flag " + key
for _, str := range defines {
key, typ, desc := parseVarNameAndType(str)
if desc == "" {
desc = "config flag " + key
}
switch typ {
case "int":
@@ -181,7 +189,12 @@ func (c *Config) LoadFlags(keys []string) (err error) {
return
}
_ = c.Set(name, f.Value.String()) // ignore error
// if f.Value implement the flag.Getter, read typed value
if gtr, ok := f.Value.(flag.Getter); ok {
_ = c.Set(name, gtr.Get())
// } else { // TIP: basic type flag always implements Getter interface
// _ = c.Set(name, f.Value.String()) // ignore error
}
})
c.fireHook(OnLoadData)

View File

@@ -22,35 +22,44 @@ type HookFunc func(event string, c *Config)
// Options config options
type Options struct {
// ParseEnv parse env in string value and default value. like: "${EnvName}" "${EnvName|default}"
// ParseEnv parse env in string value and default value. default: false
//
// - like: "${EnvName}" "${EnvName|default}"
ParseEnv bool
// ParseTime parses a duration string to time.Duration
// ParseTime parses a duration string to `time.Duration`. default: false
//
// eg: 10s, 2m
ParseTime bool
// Readonly config is readonly
Readonly bool
// ParseDefault tag on binding data to struct. tag: default
// ParseDefault tag on binding data to struct. default: false
//
// - tag: default
ParseDefault bool
// EnableCache enable config data cache
// Readonly config is readonly. default: false
Readonly bool
// EnableCache enable config data cache. default: false
EnableCache bool
// ParseKey parse key path, allow find value by key path. eg: 'key.sub' will find `map[key]sub`
// ParseKey support key path, allow find value by key path. default: true
//
// - eg: 'key.sub' will find `map[key]sub`
ParseKey bool
// TagName tag name for binding data to struct
//
// Deprecated: please set tag name by DecoderConfig, or use SetTagName()
TagName string
// Delimiter the delimiter char for split key path, if `FindByPath=true`. default is '.'
// Delimiter the delimiter char for split key path, on `ParseKey=true`.
//
// - default is '.'
Delimiter byte
// DumpFormat default write format
// DumpFormat default write format. default is 'json'
DumpFormat string
// ReadFormat default input format
// ReadFormat default input format. default is 'json'
ReadFormat string
// DecoderConfig setting for binding data to struct. such as: TagName
DecoderConfig *mapstructure.DecoderConfig
// HookFunc on data changed. you can do something...
HookFunc HookFunc
// MergeOptions settings for merge two data
MergeOptions []func(*mergo.Config)
// HookFunc on data changed. you can do something...
HookFunc HookFunc
// WatchChange bool
}

View File

@@ -99,10 +99,10 @@ func (c *Config) Data() map[string]any {
return c.data
}
// Sub return sub config data by key
// Sub return a map config data by key
func Sub(key string) map[string]any { return dc.Sub(key) }
// Sub get sub config data by key
// Sub get a map config data by key
//
// Note: will don't apply any options, like ParseEnv
func (c *Config) Sub(key string) map[string]any {
@@ -127,23 +127,24 @@ func (c *Config) Keys() []string {
}
// Get config value by key string, support get sub-value by key path(eg. 'map.key'),
//
// - ok is true, find value from config
// - ok is false, not found or error
func Get(key string, findByPath ...bool) any { return dc.Get(key, findByPath...) }
// Get config value by key
// Get config value by key, findByPath default is true.
func (c *Config) Get(key string, findByPath ...bool) any {
val, _ := c.GetValue(key, findByPath...)
return val
}
// GetValue get value by given key string.
// GetValue get value by given key string. findByPath default is true.
func GetValue(key string, findByPath ...bool) (any, bool) {
return dc.GetValue(key, findByPath...)
}
// GetValue get value by given key string.
// GetValue get value by given key string. findByPath default is true.
//
// Return:
// - ok is true, find value from config
// - ok is false, not found or error
func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) {
sep := c.opts.Delimiter
if key = formatKey(key, string(sep)); key == "" {

View File

@@ -134,20 +134,28 @@ func Getenv(name string, defVal ...string) (val string) {
return
}
func parseVarNameAndType(key string) (string, string) {
func parseVarNameAndType(key string) (string, string, string) {
var desc string
typ := "string"
key = strings.Trim(key, "-")
// can set var type: int, uint, bool
if strings.IndexByte(key, ':') > 0 {
list := strings.SplitN(key, ":", 2)
list := strings.SplitN(key, ":", 3)
key, typ = list[0], list[1]
if len(list) == 3 {
desc = list[2]
}
// if type is not valid and has multi words, as desc message.
if _, ok := validTypes[typ]; !ok {
if desc == "" && strings.ContainsRune(typ, ' ') {
desc = typ
}
typ = "string"
}
}
return key, typ
return key, typ, desc
}
// format key

View File

@@ -28,12 +28,12 @@ func (c *Config) SetData(data map[string]any) {
c.fireHook(OnSetData)
}
// Set val by key
// Set value by key. setByPath default is true
func Set(key string, val any, setByPath ...bool) error {
return dc.Set(key, val, setByPath...)
}
// Set a value by key string.
// Set a value by key string. setByPath default is true
func (c *Config) Set(key string, val any, setByPath ...bool) (err error) {
if c.opts.Readonly {
return ErrReadonly

View File

@@ -19,5 +19,6 @@
*.cov
.DS_Store
*~
testdata/
vendor/

View File

@@ -1417,6 +1417,7 @@ func BlankOr(val, defVal string) string
func ZeroOr[T ~string](val, defVal T) T
func ErrorOr(s string, err error, defVal string) string
func OrElse(s, orVal string) string
func OrElseNilSafe(s *string, orVal string) string
func OrHandle(s string, fn comdef.StringHandleFunc) string
func Valid(ss ...string) string
func Replaces(str string, pairs map[string]string) string

View File

@@ -1418,6 +1418,7 @@ func BlankOr(val, defVal string) string
func ZeroOr[T ~string](val, defVal T) T
func ErrorOr(s string, err error, defVal string) string
func OrElse(s, orVal string) string
func OrElseNilSafe(s *string, orVal string) string
func OrHandle(s string, fn comdef.StringHandleFunc) string
func Valid(ss ...string) string
func Replaces(str string, pairs map[string]string) string

View File

@@ -135,13 +135,13 @@ func Differences[T any](first, second []T, fn Comparer[T]) []T {
return CloneSlice(first)
}
max := firstLen
maxLn := firstLen
if secondLen > firstLen {
max = secondLen
maxLn = secondLen
}
result := make([]T, 0)
for i := 0; i < max; i++ {
for i := 0; i < maxLn; i++ {
if i < firstLen {
s := first[i]
if i, _ := TwowaySearch(second, s, fn); i < 0 {

View File

@@ -54,7 +54,18 @@ func MustOK(err error) {
}
}
// Must if error is not empty, will panic
// Must return like (v, error). will panic on error, otherwise return v.
//
// Usage:
//
// // old
// v, err := fn()
// if err != nil {
// panic(err)
// }
//
// // new
// v := goutil.Must(fn())
func Must[T any](v T, err error) T {
if err != nil {
panic(err)
@@ -62,6 +73,22 @@ func Must[T any](v T, err error) T {
return v
}
// MustIgnore for return like (v, error). Ignore return v and will panic on error.
//
// Useful for io, file operation func: (n int, err error)
//
// Usage:
//
// // old
// _, err := fn()
// if err != nil {
// panic(err)
// }
//
// // new
// basefn.MustIgnore(fn())
func MustIgnore(_ any, err error) { PanicErr(err) }
// ErrOnFail return input error on cond is false, otherwise return nil
func ErrOnFail(cond bool, err error) error {
return OrError(cond, err)

View File

@@ -93,10 +93,11 @@ func (b *Buffer) writeAnysWithNl(vs []any, nl bool) {
}
}
// Printf quiet write message to buffer, ignore error.
func (b *Buffer) Printf(tpl string, vs ...any) {
_, _ = b.WriteString(fmt.Sprintf(tpl, vs...))
}
// Printf quick write message to buffer, ignore error.
func (b *Buffer) Printf(tpl string, vs ...any) { _, _ = fmt.Fprintf(b, tpl, vs...) }
// Println quick write message with newline to buffer, will ignore error.
func (b *Buffer) Println(vs ...any) { _, _ = fmt.Fprintln(b, vs...) }
// ResetGet buffer string. alias of ResetAndGet()
func (b *Buffer) ResetGet() string {

View File

@@ -26,6 +26,17 @@ func IsEmpty(v any) bool {
return reflects.IsEmpty(reflect.ValueOf(v))
}
// Alias of the IsEmptyReal()
var IsZeroReal = IsEmptyReal
// IsEmptyReal checks for empty given value and also real empty value if the passed value is a pointer
func IsEmptyReal(v any) bool {
if v == nil {
return true
}
return reflects.IsEmptyReal(reflect.ValueOf(v))
}
// IsFunc value
func IsFunc(val any) bool {
if val == nil {

View File

@@ -0,0 +1,6 @@
//go:build !windows
package comdef
// Newline string for non-windows
const Newline = "\n"

View File

@@ -0,0 +1,4 @@
package comdef
// Newline string for windows
const Newline = "\r\n"

View File

@@ -59,7 +59,7 @@ type SimpleType interface {
Int | Uint | Float | ~string | ~bool
}
// ScalarType interface type.
// ScalarType basic interface type.
//
// TIP: has bool type, it cannot be ordered
//

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"fmt"
"io"
"path"
"path/filepath"
"runtime"
"strconv"
)
@@ -112,7 +112,7 @@ func (f *Func) FileLine() (file string, line int) {
func (f *Func) Location() string {
file, line := f.FileLine()
return f.Name() + "(), " + path.Base(file) + ":" + strconv.Itoa(line)
return f.Name() + "(), " + filepath.Base(file) + ":" + strconv.Itoa(line)
}
// String of the func

View File

@@ -216,14 +216,17 @@ func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error)
return // ignore I/O error
}
// names, _ := d.Readdirnames(-1)
// sort.Strings(names)
des, err := os.ReadDir(dir)
if err != nil {
return
}
// remove the last '/' char
dirLn := len(dir)
if dirLn > 1 && dir[dirLn-1] == '/' {
dir = dir[:dirLn-1]
}
for _, ent := range des {
filePath := dir + "/" + ent.Name()

View File

@@ -6,20 +6,26 @@ import (
"path/filepath"
"strings"
"github.com/gookit/goutil/basefn"
"github.com/gookit/goutil/internal/comfunc"
)
// PathSep alias of os.PathSeparator
const PathSep = os.PathSeparator
// JoinPaths elements, alias of filepath.Join()
func JoinPaths(elem ...string) string {
return filepath.Join(elem...)
}
// JoinPaths3 elements, like the filepath.Join()
func JoinPaths3(basePath, secPath string, elems ...string) string {
return comfunc.JoinPaths3(basePath, secPath, elems)
}
// JoinSubPaths elements, like the filepath.Join()
func JoinSubPaths(basePath string, elem ...string) string {
paths := make([]string, len(elem)+1)
paths[0] = basePath
copy(paths[1:], elem)
return filepath.Join(paths...)
func JoinSubPaths(basePath string, elems ...string) string {
return comfunc.JoinPaths2(basePath, elems)
}
// SlashPath alias of filepath.ToSlash
@@ -35,11 +41,21 @@ func UnixPath(path string) string {
return strings.ReplaceAll(path, "\\", "/")
}
// ToAbsPath convert process. will expand home dir
// ToAbsPath convert path to absolute path.
// Will expand home dir, if empty will return current work dir
//
// TIP: will don't check path
// TIP: will don't check path is really exists
func ToAbsPath(p string) string {
if len(p) == 0 || IsAbsPath(p) {
// return current work dir
if len(p) == 0 {
wd, err := os.Getwd()
if err != nil {
return p
}
return wd
}
if IsAbsPath(p) {
return p
}
@@ -54,3 +70,6 @@ func ToAbsPath(p string) string {
}
return filepath.Join(wd, p)
}
// Must2 ok for (any, error) result. if it has error, will panic
func Must2(_ any, err error) { basefn.MustOK(err) }

View File

@@ -2,7 +2,6 @@ package fsutil
import (
"os"
"path"
"path/filepath"
"github.com/gookit/goutil/internal/comfunc"
@@ -15,7 +14,7 @@ func DirPath(fpath string) string { return filepath.Dir(fpath) }
func Dir(fpath string) string { return filepath.Dir(fpath) }
// PathName get file/dir name from full path
func PathName(fpath string) string { return path.Base(fpath) }
func PathName(fpath string) string { return filepath.Base(fpath) }
// Name get file/dir name from full path.
//
@@ -27,25 +26,25 @@ func Name(fpath string) string {
return filepath.Base(fpath)
}
// FileExt get filename ext. alias of path.Ext()
// FileExt get filename ext. alias of filepath.Ext()
//
// eg: path/to/main.go => ".go"
func FileExt(fpath string) string { return path.Ext(fpath) }
func FileExt(fpath string) string { return filepath.Ext(fpath) }
// Extname get filename ext. alias of path.Ext()
// Extname get filename ext. alias of filepath.Ext()
//
// eg: path/to/main.go => "go"
func Extname(fpath string) string {
if ext := path.Ext(fpath); len(ext) > 0 {
if ext := filepath.Ext(fpath); len(ext) > 0 {
return ext[1:]
}
return ""
}
// Suffix get filename ext. alias of path.Ext()
// Suffix get filename ext. alias of filepath.Ext()
//
// eg: path/to/main.go => ".go"
func Suffix(fpath string) string { return path.Ext(fpath) }
func Suffix(fpath string) string { return filepath.Ext(fpath) }
// Expand will parse first `~` as user home dir path.
func Expand(pathStr string) string {

View File

@@ -6,7 +6,6 @@ import (
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
@@ -121,7 +120,7 @@ const (
//
// file, err := OpenFile("path/to/file.txt", FsCWFlags, 0666)
func OpenFile(filePath string, flag int, perm os.FileMode) (*os.File, error) {
fileDir := path.Dir(filePath)
fileDir := filepath.Dir(filePath)
if err := os.MkdirAll(fileDir, DefaultDirPerm); err != nil {
return nil, err
}
@@ -177,7 +176,7 @@ func OpenReadFile(filepath string) (*os.File, error) {
//
// CreateFile("path/to/file.txt", 0664, 0666)
func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (*os.File, error) {
dirPath := path.Dir(fpath)
dirPath := filepath.Dir(fpath)
if !IsDir(dirPath) {
err := os.MkdirAll(dirPath, dirPerm)
if err != nil {

View File

@@ -27,9 +27,7 @@ func DiscardReader(src io.Reader) {
}
// ReadFile read file contents, will panic on error
func ReadFile(filePath string) []byte {
return MustReadFile(filePath)
}
func ReadFile(filePath string) []byte { return MustReadFile(filePath) }
// MustReadFile read file contents, will panic on error
func MustReadFile(filePath string) []byte {
@@ -53,9 +51,7 @@ func MustReadReader(r io.Reader) []byte {
}
// ReadString read contents from path or io.Reader, will panic on in type error
func ReadString(in any) string {
return string(GetContents(in))
}
func ReadString(in any) string { return string(GetContents(in)) }
// ReadStringOrErr read contents from path or io.Reader, will panic on in type error
func ReadStringOrErr(in any) (string, error) {
@@ -78,9 +74,7 @@ func ReadAll(in any) []byte { return MustRead(in) }
func GetContents(in any) []byte { return MustRead(in) }
// MustRead read contents from path or io.Reader, will panic on in type error
func MustRead(in any) []byte {
return basefn.Must(ReadOrErr(in))
}
func MustRead(in any) []byte { return basefn.Must(ReadOrErr(in)) }
// ReadOrErr read contents from path or io.Reader, will panic on in type error
func ReadOrErr(in any) ([]byte, error) {

View File

@@ -83,6 +83,7 @@ func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error {
// Usage:
//
// fsutil.PutContents(filePath, contents, fsutil.FsCWAFlags) // append write
// fsutil.Must2(fsutil.PutContents(filePath, contents)) // panic on error
func PutContents(filePath string, data any, fileFlag ...int) (int, error) {
f, err := QuickOpenFile(filePath, basefn.FirstOr(fileFlag, FsCWTFlags))
if err != nil {

View File

@@ -1,7 +1,7 @@
package goinfo
import (
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -91,7 +91,7 @@ func GetCallersInfo(skip, max int) []string {
if strings.ContainsRune(file, '/') {
name = fc.Name()
file = path.Base(file)
file = filepath.Base(file)
// eg: github.com/gookit/goutil/goinfo_test.someFunc2(),stack_test.go:26
callers = append(callers, name+"(),"+file+":"+strconv.Itoa(line))
}

View File

@@ -23,28 +23,50 @@ func PanicIf(cond bool, fmtAndArgs ...any) {
basefn.PanicIf(cond, fmtAndArgs...)
}
// PanicIfErr if error is not empty, will panic
func PanicIfErr(err error) {
if err != nil {
panic(err)
}
}
// PanicErr if error is not empty, will panic
// PanicErr if error is not empty, will panic.
// Alias of basefn.PanicErr()
func PanicErr(err error) {
if err != nil {
panic(err)
}
}
// MustOK if error is not empty, will panic
func MustOK(err error) {
if err != nil {
panic(err)
}
}
// PanicIfErr if error is not empty, will panic.
// Alias of basefn.PanicErr()
func PanicIfErr(err error) { PanicErr(err) }
// Must if error is not empty, will panic
// MustOK if error is not empty, will panic.
// Alias of basefn.MustOK()
func MustOK(err error) { PanicErr(err) }
// MustIgnore for return like (v, error). Ignore return v and will panic on error.
//
// Useful for io, file operation func: (n int, err error)
//
// Usage:
//
// // old
// _, err := fn()
// if err != nil {
// panic(err)
// }
//
// // new
// goutil.MustIgnore(fn())
func MustIgnore(_ any, err error) { PanicErr(err) }
// Must return like (v, error). will panic on error, otherwise return v.
//
// Usage:
//
// // old
// v, err := fn()
// if err != nil {
// panic(err)
// }
//
// // new
// v := goutil.Must(fn())
func Must[T any](v T, err error) T {
if err != nil {
panic(err)

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"reflect"
"regexp"
"strings"
)
@@ -106,3 +107,19 @@ func Contains(data, elem any) (valid, found bool) {
}
return true, false
}
// StringsContains check string slice contains string
func StringsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// check is number: int or float
var numReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
// IsNumeric returns true if the given string is a numeric, otherwise false.
func IsNumeric(s string) bool { return numReg.MatchString(s) }

View File

@@ -0,0 +1,20 @@
package comfunc
import "path/filepath"
// JoinPaths2 elements, like the filepath.Join()
func JoinPaths2(basePath string, elems []string) string {
paths := make([]string, len(elems)+1)
paths[0] = basePath
copy(paths[1:], elems)
return filepath.Join(paths...)
}
// JoinPaths3 elements, like the filepath.Join()
func JoinPaths3(basePath, secPath string, elems []string) string {
paths := make([]string, len(elems)+2)
paths[0] = basePath
paths[1] = secPath
copy(paths[2:], elems)
return filepath.Join(paths...)
}

View File

@@ -5,6 +5,7 @@ import (
"strconv"
"strings"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/reflects"
)
@@ -193,6 +194,14 @@ func Keys(mp any) (keys []string) {
return
}
// TypedKeys get all keys of the given typed map.
func TypedKeys[K comdef.SimpleType, V any](mp map[K]V) (keys []K) {
for key := range mp {
keys = append(keys, key)
}
return
}
// Values get all values from the given map.
func Values(mp any) (values []any) {
rv := reflect.Indirect(reflect.ValueOf(mp))
@@ -207,6 +216,14 @@ func Values(mp any) (values []any) {
return
}
// TypedValues get all values from the given typed map.
func TypedValues[K comdef.SimpleType, V any](mp map[K]V) (values []V) {
for _, val := range mp {
values = append(values, val)
}
return
}
// EachAnyMap iterates the given map and calls the given function for each item.
func EachAnyMap(mp any, fn func(key string, val any)) {
rv := reflect.Indirect(reflect.ValueOf(mp))
@@ -218,3 +235,10 @@ func EachAnyMap(mp any, fn func(key string, val any)) {
fn(key.String(), rv.MapIndex(key).Interface())
}
}
// EachTypedMap iterates the given map and calls the given function for each item.
func EachTypedMap[K comdef.SimpleType, V any](mp map[K]V, fn func(key K, val V)) {
for key, val := range mp {
fn(key, val)
}
}

View File

@@ -39,6 +39,17 @@ func SimpleMerge(src, dst map[string]any) map[string]any {
return dst
}
// Merge1level merge multi any map[string]any data. only merge one level data.
func Merge1level(mps ...map[string]any) map[string]any {
newMp := make(map[string]any)
for _, mp := range mps {
for k, v := range mp {
newMp[k] = v
}
}
return newMp
}
// func DeepMerge(src, dst map[string]any, deep int) map[string]any { TODO
// }

11
vendor/github.com/gookit/goutil/mathutil/calc.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package mathutil
import "github.com/gookit/goutil/comdef"
// Abs get absolute value of given value
func Abs[T comdef.Int](val T) T {
if val >= 0 {
return val
}
return -val
}

View File

@@ -192,7 +192,16 @@ func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) {
iVal = int(tVal)
}
case string:
iVal, err = strconv.Atoi(strings.TrimSpace(tVal))
sVal := strings.TrimSpace(tVal)
iVal, err = strconv.Atoi(sVal)
// handle the case where the string might be a float
if err != nil && checkfn.IsNumeric(sVal) {
var floatVal float64
if floatVal, err = strconv.ParseFloat(sVal, 64); err == nil {
iVal = int(math.Round(floatVal))
err = nil
}
}
case comdef.Int64able: // eg: json.Number
var i64 int64
if i64, err = tVal.Int64(); err == nil {
@@ -287,7 +296,16 @@ func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) {
switch tVal := in.(type) {
case string:
i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0)
sVal := strings.TrimSpace(tVal)
i64, err = strconv.ParseInt(sVal, 10, 0)
// handle the case where the string might be a float
if err != nil && checkfn.IsNumeric(sVal) {
var floatVal float64
if floatVal, err = strconv.ParseFloat(sVal, 64); err == nil {
i64 = int64(math.Round(floatVal))
err = nil
}
}
case int:
i64 = int64(tVal)
case int8:

Some files were not shown because too many files have changed in this diff Show More