Compare commits

..

45 Commits

Author SHA1 Message Date
fschade
2883a6245d enhancement: add timeoutListener config 2025-11-04 16:45:04 +01:00
Jörn Friedrich Dreyer
936a84096e introduce read http timeout listener
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2025-11-04 14:39:55 +01:00
dependabot[bot]
6e0bb09aff build(deps): bump github.com/onsi/ginkgo/v2 from 2.27.1 to 2.27.2
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.27.1 to 2.27.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.27.1...v2.27.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-version: 2.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-04 12:51:49 +01:00
Viktor Scharf
59eb411024 correct STORAGE_USERS_POSIX_WATCH_FS env typo in CI (#1746)
* correct env typo

* set STORAGE_USERS_POSIX_SCAN_DEBOUNCE_DELAY=0

* tests: wait for postprocessing to finish before accessing files

---------

Co-authored-by: Ralf Haferkamp <r.haferkamp@opencloud.eu>
2025-11-04 10:22:06 +01:00
OpenCloud Devops
b53b4ef1de 🎉 Release 3.7.0 (#1723)
* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0

* 🎉 Release 3.7.0
2025-11-03 16:27:10 +01:00
Viktor Scharf
c05c740fa6 bump web 4.2.0 (#1765) 2025-11-03 15:55:36 +01:00
Artur Neumann
5b98860585 check status of postprocessing before accesing the file (#1762) 2025-11-03 14:13:46 +01:00
Benedikt Kulmann
a3c3b6a07c Merge pull request #1755 from opencloud-eu/update-gh-labels
chore: update labels
2025-11-03 13:43:44 +01:00
opencloudeu
790c6b165f [tx] updated from transifex 2025-11-02 00:03:10 +00:00
opencloudeu
a2935abe3d [tx] updated from transifex 2025-11-01 00:02:51 +00:00
Benedikt Kulmann
a4856b4a80 chore: update labels 2025-10-31 15:50:26 +01:00
Ralf Haferkamp
b5b15f29de bump reva
fixes: #1747
2025-10-30 17:17:27 +01:00
opencloudeu
e270cdbfd2 [tx] updated from transifex 2025-10-29 00:03:02 +00:00
Ralf Haferkamp
e2441696c2 graph(education): 'primaryRole' and 'identities' should be optional
Related: #1597
2025-10-28 12:56:42 +01:00
Ralf Haferkamp
28ec9c3282 graph(education): Make 'schoolNumber' attribute optional
It's already optional in the spec. For mulit-tenant provisioning
we want it to be optional as well.

Related: #1597
2025-10-28 12:56:42 +01:00
Shawn Wilsher
920a6916c4 fix: only search LDAP group by name 2025-10-28 10:22:54 +01:00
Thomas Schweiger
10e77768a5 Merge pull request #1708 from opencloud-eu/schweigisito/issue1698
fix: fix #1698 - Notification email doesn't contain Message-Id header
2025-10-27 20:59:06 +01:00
Thomas Schweiger
e7a4cbaae5 fix: fix #1698 - Notification email doesn't contain Message-ID header 2025-10-27 18:56:37 +01:00
OpenCloud Devops
e62e2e0f12 🎉 Release 3.6.0 (#1537)
* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.5.1

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0

* 🎉 Release 3.6.0
2025-10-27 16:26:38 +01:00
dependabot[bot]
9cb973baac build(deps): bump github.com/onsi/ginkgo/v2 from 2.26.0 to 2.27.1
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.26.0 to 2.27.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.26.0...v2.27.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-version: 2.27.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 16:16:33 +01:00
Viktor Scharf
9e16bb9e29 bump-version-v3.6.0 (#1719) 2025-10-27 15:03:02 +01:00
Jörn Friedrich Dreyer
641dac0a88 Merge pull request #1718 from opencloud-eu/revaBump-2.39.1
revaBump-2.39.1
2025-10-27 14:12:01 +01:00
Viktor Scharf
570ec0bf97 revaBump-2.39.1 2025-10-27 13:30:13 +01:00
Jörn Friedrich Dreyer
aaaf5cf5c4 Merge pull request #1622 from opencloud-eu/shutdownorder
allow specifying a shutdown order
2025-10-27 13:13:04 +01:00
Florian Schade
fb8af22073 chore: bump reva (#1701)
* chore: bump reva

* enhancement(test): add postprocessing wait helper
2025-10-27 12:01:27 +01:00
Jörn Friedrich Dreyer
8c9f266ded allow specifying a shutdown order
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2025-10-27 11:58:14 +01:00
Viktor Scharf
f04f6ad470 [full-ci] feat: implement OIDC authentication option (#1676)
* feat: implement Bearer Token authentication option

* fix
2025-10-27 11:17:44 +01:00
opencloudeu
c887947a85 [tx] updated from transifex 2025-10-27 00:01:43 +00:00
opencloudeu
ac8be264f0 [tx] updated from transifex 2025-10-26 00:01:09 +00:00
Thomas Schweiger
2c18d5b010 fix: apply changes from other fixes in compose repo (#1707)
* fix: apply changes from other fixes in compose repo

* temporarily disabled e2e navigation step

---------

Co-authored-by: Viktor Scharf <v.scharf@opencloud.eu>
2025-10-24 22:10:27 +02:00
Viktor Scharf
44ee182aa3 apiTest-coverage for #1523 (#1660)
* apiTest-coverage for #1523

* check propfind contans correct files name

* bump reva for getting #381
2025-10-24 09:45:03 +02:00
dependabot[bot]
d76cacd99f build(deps): bump github.com/kovidgoyal/imaging from 1.6.4 to 1.7.2
Bumps [github.com/kovidgoyal/imaging](https://github.com/kovidgoyal/imaging) from 1.6.4 to 1.7.2.
- [Release notes](https://github.com/kovidgoyal/imaging/releases)
- [Changelog](https://github.com/kovidgoyal/imaging/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/kovidgoyal/imaging/compare/v1.6.4...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/kovidgoyal/imaging
  dependency-version: 1.7.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 17:48:43 +02:00
dependabot[bot]
fb94f34a1f build(deps): bump github.com/blevesearch/bleve/v2 from 2.5.3 to 2.5.4
Bumps [github.com/blevesearch/bleve/v2](https://github.com/blevesearch/bleve) from 2.5.3 to 2.5.4.
- [Release notes](https://github.com/blevesearch/bleve/releases)
- [Commits](https://github.com/blevesearch/bleve/compare/v2.5.3...v2.5.4)

---
updated-dependencies:
- dependency-name: github.com/blevesearch/bleve/v2
  dependency-version: 2.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 17:47:05 +02:00
dependabot[bot]
0b4c9becfb build(deps): bump golang.org/x/oauth2 from 0.31.0 to 0.32.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.31.0...v0.32.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 10:54:58 +02:00
dependabot[bot]
c362d84f11 build(deps): bump golang.org/x/net from 0.44.0 to 0.46.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.44.0 to 0.46.0.
- [Commits](https://github.com/golang/net/compare/v0.44.0...v0.46.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 09:56:50 +02:00
opencloudeu
6e4e533e2c [tx] updated from transifex 2025-10-23 00:01:41 +00:00
Alex
f12a433b85 chore: add groupware capabilities (#1661) 2025-10-21 22:31:45 +02:00
Viktor Scharf
9108188e8a revaBumb: add groupware capabilities (#1689) 2025-10-21 14:27:26 +02:00
Viktor Scharf
18a6927b79 revaUpdate: adding groupware capabilities (#1659) 2025-10-20 19:19:12 +02:00
opencloudeu
3874c3e0d3 [tx] updated from transifex 2025-10-20 00:02:20 +00:00
opencloudeu
254f999382 [tx] updated from transifex 2025-10-16 08:04:58 +00:00
Ralf Haferkamp
b099dcdf6e fix: 'readyz' enpdoint check only required service
The readyz endpoint contained unconditional checks for the LDAP and nats
endpoints. Depending on configuration neihter LDAP nor NATS might be
required.
2025-10-14 16:49:46 +02:00
Ralf Haferkamp
d1a6cc22c1 graph: Allow the service to run without a NatsJS server
For running the Graph Education API we want to keep it possible
to run the service standalone. With an LDAP server as the only
external dependency.
2025-10-14 16:49:46 +02:00
Viktor Scharf
6c387336e1 chore/bump-web-4.1.0 (#1652)
CI shows a false failure. merge it
2025-10-13 16:09:44 +02:00
Ralf Haferkamp
805bd4305e bump reva to lastest main
for https://github.com/opencloud-eu/reva/pull/372
2025-10-13 15:22:00 +02:00
339 changed files with 11534 additions and 3021 deletions

View File

@@ -1 +1,4 @@
_extends: gh-labels

View File

@@ -1,4 +1,4 @@
# The test runner source for UI tests
WEB_COMMITID=1abc9f6c9bf8b5fe6c8e9cb8418dab7911edaec7
WEB_COMMITID=6abffcc9cff31c46a341105eb6030fec56338126
WEB_BRANCH=main

View File

@@ -338,6 +338,7 @@ config = {
"FRONTEND_READONLY_USER_ATTRIBUTES": "user.onPremisesSamAccountName,user.displayName,user.mail,user.passwordProfile,user.accountEnabled,user.appRoleAssignments",
"OC_LDAP_SERVER_WRITE_ENABLED": False,
"OC_EXCLUDE_RUN_SERVICES": "idm",
"OC_LDAP_USER_ENABLED_ATTRIBUTE": "",
},
},
},
@@ -350,7 +351,7 @@ config = {
"part": {
"skip": False,
"totalParts": 4, # divide and run all suites in parts (divide pipelines)
"xsuites": ["search", "app-provider", "app-provider-onlyOffice", "app-store", "keycloak", "oidc", "ocm", "a11y", "mobile-view"], # suites to skip
"xsuites": ["search", "app-provider", "app-provider-onlyOffice", "app-store", "keycloak", "oidc", "ocm", "a11y", "mobile-view", "navigation"], # suites to skip
},
"search": {
"skip": False,
@@ -1076,6 +1077,7 @@ def localApiTests(name, suites, storage = "decomposed", extra_environment = {},
"WITH_REMOTE_PHP": with_remote_php,
"COLLABORATION_SERVICE_URL": "http://wopi-fakeoffice:9300",
"OC_STORAGE_PATH": "$HOME/.opencloud/storage/users",
"USE_BEARER_TOKEN": True,
}
for item in extra_environment:
@@ -1479,7 +1481,7 @@ def multiServiceE2ePipeline(ctx, watch_fs_enabled = False):
}
if watch_fs_enabled:
extra_server_environment["STORAGE_USES_POSIX_WATCH_FS"] = True
extra_server_environment["STORAGE_USERS_POSIX_WATCH_FS"] = True
storage_users_environment = {
"OC_CORS_ALLOW_ORIGINS": "%s,https://%s:9201" % (OC_URL, OC_SERVER_NAME),
@@ -2071,6 +2073,7 @@ def opencloudServer(storage = "decomposed", accounts_hash_difficulty = 4, depend
"WEB_DEBUG_ADDR": "0.0.0.0:9104",
"WEBDAV_DEBUG_ADDR": "0.0.0.0:9119",
"WEBFINGER_DEBUG_ADDR": "0.0.0.0:9279",
"STORAGE_USERS_POSIX_SCAN_DEBOUNCE_DELAY": 0,
}
if storage == "posix":
@@ -2108,7 +2111,7 @@ def opencloudServer(storage = "decomposed", accounts_hash_difficulty = 4, depend
environment["SEARCH_EXTRACTOR_CS3SOURCE_INSECURE"] = True
if watch_fs_enabled:
environment["STORAGE_USES_POSIX_WATCH_FS"] = True
environment["STORAGE_USERS_POSIX_WATCH_FS"] = True
# Pass in "default" accounts_hash_difficulty to not set this environment variable.
# That will allow OpenCloud to use whatever its built-in default is.

View File

@@ -1,5 +1,103 @@
# Changelog
## [3.7.0](https://github.com/opencloud-eu/opencloud/releases/tag/v3.7.0) - 2025-11-03
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @individual-it, @kulmann, @rhafer, @schweigisito, @sdwilsh
### ✅ Tests
- check status of postprocessing before accesing the file [[#1762](https://github.com/opencloud-eu/opencloud/pull/1762)]
### 📈 Enhancement
- multi-tenancy: Optional attributes on provision API [[#1663](https://github.com/opencloud-eu/opencloud/pull/1663)]
- fix: fix #1698 - Notification email doesn't contain Message-Id header [[#1708](https://github.com/opencloud-eu/opencloud/pull/1708)]
### 🐛 Bug Fixes
- fix: only search LDAP group by name [[#1724](https://github.com/opencloud-eu/opencloud/pull/1724)]
### 📦️ Dependencies
- [full-ci] bump web 4.2.0 and opencloud 3.7.0 version [[#1765](https://github.com/opencloud-eu/opencloud/pull/1765)]
## [3.6.0](https://github.com/opencloud-eu/opencloud/releases/tag/v3.6.0) - 2025-10-27
### ❤️ Thanks to all contributors! ❤️
@AlexAndBear, @ScharfViktor, @butonic, @dragonchaser, @fschade, @micbar, @prashant-gurung899, @rhafer, @schweigisito, @tammi-23
### 📈 Enhancement
- allow specifying a shutdown order [[#1622](https://github.com/opencloud-eu/opencloud/pull/1622)]
- change: use 404 as status when thumbnail can not be fetched [[#1582](https://github.com/opencloud-eu/opencloud/pull/1582)]
- feat: add dedicated logo (web) for mobile view to theme [[#1579](https://github.com/opencloud-eu/opencloud/pull/1579)]
- feat: make it possible to start the collaboration service in the single process [[#1569](https://github.com/opencloud-eu/opencloud/pull/1569)]
- introduce AppURLs helper for atomic backgroud updates [[#1542](https://github.com/opencloud-eu/opencloud/pull/1542)]
- chore: add config for capability CheckForUpdates [[#1556](https://github.com/opencloud-eu/opencloud/pull/1556)]
### ✅ Tests
- [full-ci] feat: implement OIDC authentication option [[#1676](https://github.com/opencloud-eu/opencloud/pull/1676)]
- apiTest-coverage for #1523 [[#1660](https://github.com/opencloud-eu/opencloud/pull/1660)]
- [full-ci] deleted unused step definitions [[#1639](https://github.com/opencloud-eu/opencloud/pull/1639)]
- check thumbnails in the share with me response [[#1605](https://github.com/opencloud-eu/opencloud/pull/1605)]
- [full-ci][tests-only] fix restore browsers cache workflow [[#1615](https://github.com/opencloud-eu/opencloud/pull/1615)]
- [full-ci] Enhance getSpaceByName: check local cache before Graph API calls [[#1574](https://github.com/opencloud-eu/opencloud/pull/1574)]
- [full-ci] getting personal space by userId instead of userName [[#1553](https://github.com/opencloud-eu/opencloud/pull/1553)]
- apiTest-flaky: sync share before checking [[#1550](https://github.com/opencloud-eu/opencloud/pull/1550)]
- [decomposed] use Alpine for opencloud starting [[#1547](https://github.com/opencloud-eu/opencloud/pull/1547)]
### 🐛 Bug Fixes
- fix: apply changes from other fixes in compose repo [[#1707](https://github.com/opencloud-eu/opencloud/pull/1707)]
- fix(settings): env var precedence [[#1625](https://github.com/opencloud-eu/opencloud/pull/1625)]
- fix(antivirus): update icap-client library which fixes tcp socket reuse [[#1589](https://github.com/opencloud-eu/opencloud/pull/1589)]
- fix: use valid autocomplete values (axe autocomplete-valid) [[#1588](https://github.com/opencloud-eu/opencloud/pull/1588)]
- Fix collaboration service name [[#1577](https://github.com/opencloud-eu/opencloud/pull/1577)]
- let the runtime always create a cancel context [[#1565](https://github.com/opencloud-eu/opencloud/pull/1565)]
- Bump reva and cs3apis [[#1538](https://github.com/opencloud-eu/opencloud/pull/1538)]
- use correct endpoint in nats check [[#1533](https://github.com/opencloud-eu/opencloud/pull/1533)]
### 📚 Documentation
- adr: use eduation api for multi-tenancy provisioning [[#1548](https://github.com/opencloud-eu/opencloud/pull/1548)]
- fix: remove deprecated web ui feature "OpenAppsInTab" [[#1575](https://github.com/opencloud-eu/opencloud/pull/1575)]
### 📦️ Dependencies
- build(deps): bump github.com/onsi/ginkgo/v2 from 2.26.0 to 2.27.1 [[#1705](https://github.com/opencloud-eu/opencloud/pull/1705)]
- [decomposed] bump-version-v3.6.0 [[#1719](https://github.com/opencloud-eu/opencloud/pull/1719)]
- revaBump-2.39.1 [[#1718](https://github.com/opencloud-eu/opencloud/pull/1718)]
- chore: bump reva [[#1701](https://github.com/opencloud-eu/opencloud/pull/1701)]
- build(deps): bump github.com/kovidgoyal/imaging from 1.6.4 to 1.7.2 [[#1696](https://github.com/opencloud-eu/opencloud/pull/1696)]
- build(deps): bump github.com/blevesearch/bleve/v2 from 2.5.3 to 2.5.4 [[#1697](https://github.com/opencloud-eu/opencloud/pull/1697)]
- build(deps): bump golang.org/x/oauth2 from 0.31.0 to 0.32.0 [[#1634](https://github.com/opencloud-eu/opencloud/pull/1634)]
- build(deps): bump golang.org/x/net from 0.44.0 to 0.46.0 [[#1638](https://github.com/opencloud-eu/opencloud/pull/1638)]
- revaBumb: add groupware capabilities [[#1689](https://github.com/opencloud-eu/opencloud/pull/1689)]
- revaUpdate: adding groupware capabilities [[#1659](https://github.com/opencloud-eu/opencloud/pull/1659)]
- chore/bump-web-4.1.0 [[#1652](https://github.com/opencloud-eu/opencloud/pull/1652)]
- build(deps): bump google.golang.org/grpc from 1.75.1 to 1.76.0 [[#1628](https://github.com/opencloud-eu/opencloud/pull/1628)]
- build(deps): bump github.com/coreos/go-oidc/v3 from 3.15.0 to 3.16.0 [[#1627](https://github.com/opencloud-eu/opencloud/pull/1627)]
- build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 from 2.27.2 to 2.27.3 [[#1608](https://github.com/opencloud-eu/opencloud/pull/1608)]
- build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.11 to 3.4.12 [[#1609](https://github.com/opencloud-eu/opencloud/pull/1609)]
- build(deps): bump google.golang.org/protobuf from 1.36.9 to 1.36.10 [[#1604](https://github.com/opencloud-eu/opencloud/pull/1604)]
- build(deps): bump github.com/onsi/ginkgo/v2 from 2.25.3 to 2.26.0 [[#1603](https://github.com/opencloud-eu/opencloud/pull/1603)]
- build(deps): bump github.com/nats-io/nats.go from 1.46.0 to 1.46.1 [[#1590](https://github.com/opencloud-eu/opencloud/pull/1590)]
- build(deps): bump github.com/olekukonko/tablewriter from 1.0.9 to 1.1.0 [[#1584](https://github.com/opencloud-eu/opencloud/pull/1584)]
- build(deps): bump github.com/open-policy-agent/opa from 1.8.0 to 1.9.0 [[#1576](https://github.com/opencloud-eu/opencloud/pull/1576)]
- build(deps): bump github.com/nats-io/nats-server/v2 from 2.11.9 to 2.12.0 [[#1568](https://github.com/opencloud-eu/opencloud/pull/1568)]
- build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 [[#1567](https://github.com/opencloud-eu/opencloud/pull/1567)]
- reva bump. getting #327 [[#1555](https://github.com/opencloud-eu/opencloud/pull/1555)]
- build(deps): bump golang.org/x/image from 0.30.0 to 0.31.0 [[#1552](https://github.com/opencloud-eu/opencloud/pull/1552)]
- build(deps): bump github.com/nats-io/nats.go from 1.45.0 to 1.46.0 [[#1551](https://github.com/opencloud-eu/opencloud/pull/1551)]
- build(deps): bump golang.org/x/crypto from 0.41.0 to 0.42.0 [[#1545](https://github.com/opencloud-eu/opencloud/pull/1545)]
- build(deps): bump github.com/testcontainers/testcontainers-go/modules/opensearch from 0.38.0 to 0.39.0 [[#1544](https://github.com/opencloud-eu/opencloud/pull/1544)]
- build(deps): bump github.com/open-policy-agent/opa from 1.6.0 to 1.8.0 [[#1510](https://github.com/opencloud-eu/opencloud/pull/1510)]
- build(deps): bump google.golang.org/grpc from 1.75.0 to 1.75.1 [[#1534](https://github.com/opencloud-eu/opencloud/pull/1534)]
## [3.5.0](https://github.com/opencloud-eu/opencloud/releases/tag/v3.5.0) - 2025-09-22
### ❤️ Thanks to all contributors! ❤️

View File

@@ -663,6 +663,7 @@
"profile",
"roles",
"groups",
"OpenCloudUnique_ID",
"basic",
"email"
],
@@ -2308,7 +2309,7 @@
"always"
],
"usePasswordModifyExtendedOp": [
"false"
"true"
],
"trustEmail": [
"false"

43
go.mod
View File

@@ -11,7 +11,7 @@ require (
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/bbalet/stopwords v1.0.0
github.com/beevik/etree v1.6.0
github.com/blevesearch/bleve/v2 v2.5.3
github.com/blevesearch/bleve/v2 v2.5.4
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.16.0
github.com/cs3org/go-cs3apis v0.0.0-20250908152307-4ca807afe54e
@@ -33,7 +33,7 @@ require (
github.com/go-micro/plugins/v4/store/nats-js-kv v0.0.0-20240726082623-6831adfdcdc4
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
github.com/go-playground/validator/v10 v10.27.0
github.com/go-playground/validator/v10 v10.28.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/golang/protobuf v1.5.4
@@ -48,7 +48,7 @@ require (
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jinzhu/now v1.1.5
github.com/justinas/alice v1.2.0
github.com/kovidgoyal/imaging v1.6.4
github.com/kovidgoyal/imaging v1.7.2
github.com/leonelquinteros/gotext v1.7.2
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.66.0
@@ -56,16 +56,16 @@ require (
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.12.0
github.com/nats-io/nats.go v1.46.1
github.com/nats-io/nats.go v1.47.0
github.com/oklog/run v1.2.0
github.com/olekukonko/tablewriter v1.1.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.26.0
github.com/onsi/ginkgo/v2 v2.27.2
github.com/onsi/gomega v1.38.2
github.com/open-policy-agent/opa v1.9.0
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
github.com/opencloud-eu/reva/v2 v2.38.1-0.20251002093930-dcce351c08d6
github.com/opencloud-eu/reva/v2 v2.39.2-0.20251030154544-cac8a0257da6
github.com/opensearch-project/opensearch-go/v4 v4.5.0
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
@@ -102,14 +102,14 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto v0.42.0
golang.org/x/crypto v0.43.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
golang.org/x/image v0.31.0
golang.org/x/net v0.44.0
golang.org/x/oauth2 v0.31.0
golang.org/x/image v0.32.0
golang.org/x/net v0.46.0
golang.org/x/oauth2 v0.32.0
golang.org/x/sync v0.17.0
golang.org/x/term v0.35.0
golang.org/x/text v0.29.0
golang.org/x/term v0.36.0
golang.org/x/text v0.30.0
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10
@@ -140,13 +140,13 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/bleve_index_api v1.2.10 // indirect
github.com/blevesearch/geo v0.2.4 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.12 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
@@ -156,7 +156,7 @@ require (
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/blevesearch/zapx/v16 v16.2.6 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -229,7 +229,7 @@ require (
github.com/gobwas/ws v1.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -257,6 +257,7 @@ require (
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/kovidgoyal/go-parallel v1.0.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/dsig v1.0.0 // indirect
@@ -327,6 +328,7 @@ require (
github.com/rs/xid v1.6.0 // indirect
github.com/russellhaering/goxmldsig v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/segmentio/kafka-go v0.4.49 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
@@ -334,7 +336,7 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect
github.com/sethvargo/go-diceware v0.5.0 // indirect
github.com/sethvargo/go-password v0.3.1 // indirect
github.com/shamaton/msgpack/v2 v2.3.1 // indirect
github.com/shamaton/msgpack/v2 v2.4.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
@@ -369,15 +371,14 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/time v0.13.0 // indirect
golang.org/x/tools v0.36.0 // indirect
golang.org/x/tools v0.37.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect

94
go.sum
View File

@@ -151,10 +151,10 @@ github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blevesearch/bleve/v2 v2.5.3 h1:9l1xtKaETv64SZc1jc4Sy0N804laSa/LeMbYddq1YEM=
github.com/blevesearch/bleve/v2 v2.5.3/go.mod h1:Z/e8aWjiq8HeX+nW8qROSxiE0830yQA071dwR3yoMzw=
github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/bleve/v2 v2.5.4 h1:1iur8e+PHsxtncV2xIVuqlQme/V8guEDO2uV6Wll3lQ=
github.com/blevesearch/bleve/v2 v2.5.4/go.mod h1:yB4PnV4N2q5rTEpB2ndG8N2ISexBQEFIYgwx4ztfvoo=
github.com/blevesearch/bleve_index_api v1.2.10 h1:FMFmZCmTX6PdoLLvwUnKF2RsmILFFwO3h0WPevXY9fE=
github.com/blevesearch/bleve_index_api v1.2.10/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
@@ -165,8 +165,8 @@ github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZG
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/scorch_segment_api/v2 v2.3.12 h1:GGZc2qwbyRBwtckPPkHkLyXw64mmsLJxdturBI1cM+c=
github.com/blevesearch/scorch_segment_api/v2 v2.3.12/go.mod h1:JBRGAneqgLSI2+jCNjtwMqp2B7EBF3/VUzgDPIU33MM=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
@@ -185,8 +185,8 @@ github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww=
github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs=
github.com/blevesearch/zapx/v16 v16.2.6 h1:OHuUl2GhM+FpBq9RwNsJ4k/QodqbMMHoQEgn/IHYpu8=
github.com/blevesearch/zapx/v16 v16.2.6/go.mod h1:cuAPB+YoIyRngNhno1S1GPr9SfMk+x/SgAHBLXSIq3k=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
@@ -361,8 +361,8 @@ github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BN
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo=
github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc=
@@ -447,8 +447,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
@@ -480,8 +480,8 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
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=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -729,8 +729,10 @@ github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXH
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=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kovidgoyal/imaging v1.6.4 h1:K0idhRPXnRrJBKnBYcTfI1HTWSNDeAn7hYDvf9I0dCk=
github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnpB5EPosRfRY=
github.com/kovidgoyal/go-parallel v1.0.1 h1:nYUjN+EdpbmQjTg3N5eTUInuXTB3/1oD2vHdaMfuHoI=
github.com/kovidgoyal/go-parallel v1.0.1/go.mod h1:BJNIbe6+hxyFWv7n6oEDPj3PA5qSw5OCtf0hcVxWJiw=
github.com/kovidgoyal/imaging v1.7.2 h1:mmT6k6Az3mC6dbqdZ6Q9KQCdZFWTAQ+q97NyGZgJ/2c=
github.com/kovidgoyal/imaging v1.7.2/go.mod h1:GdkCORjfZMMGFY0Pb7TDmRhj7PDhxF/QShKukSCj0VU=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -899,8 +901,8 @@ github.com/nats-io/jwt/v2 v2.8.0 h1:K7uzyz50+yGZDO5o772eRE7atlcSEENpL7P+b74JV1g=
github.com/nats-io/jwt/v2 v2.8.0/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
github.com/nats-io/nats-server/v2 v2.12.0 h1:OIwe8jZUqJFrh+hhiyKu8snNib66qsx806OslqJuo74=
github.com/nats-io/nats-server/v2 v2.12.0/go.mod h1:nr8dhzqkP5E/lDwmn+A2CvQPMd1yDKXQI7iGg3lAvww=
github.com/nats-io/nats.go v1.46.1 h1:bqQ2ZcxVd2lpYI97xYASeRTY3I5boe/IVmuUDPitHfo=
github.com/nats-io/nats.go v1.46.1/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
github.com/nats-io/nats.go v1.47.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=
@@ -931,8 +933,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE=
github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
@@ -946,8 +948,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.38.1-0.20251002093930-dcce351c08d6 h1:b/agGaz/lQtZ8rikiqf4onpdpdllcUez/NO2pDWhEuU=
github.com/opencloud-eu/reva/v2 v2.38.1-0.20251002093930-dcce351c08d6/go.mod h1:kv+7Jfn0uqAg4Wy5rX4XuT5aX7DKvbtGp9hVcsES2+M=
github.com/opencloud-eu/reva/v2 v2.39.2-0.20251030154544-cac8a0257da6 h1:BUrCUrRqBg04MJuhnIK4H1KNK4aebK6H/AYcHjQ0DM4=
github.com/opencloud-eu/reva/v2 v2.39.2-0.20251030154544-cac8a0257da6/go.mod h1:Qm0CibFYrFc096OhWWL14nsGiFoE6g/4oMFHV5CqU+Q=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -999,8 +1001,6 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om
github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=
github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/alertmanager v0.28.1 h1:BK5pCoAtaKg01BYRUJhEDV1tqJMEtYBGzPw8QdvnnvA=
github.com/prometheus/alertmanager v0.28.1/go.mod h1:0StpPUDDHi1VXeM7p2yYfeZgLVi/PPlt39vo9LQUHxM=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -1079,6 +1079,8 @@ github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvD
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
@@ -1097,8 +1099,8 @@ github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIeh
github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw=
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shamaton/msgpack/v2 v2.3.1 h1:R3QNLIGA/tbdczNMZ5PCRxrXvy+fnzsIaHG4kKMgWYo=
github.com/shamaton/msgpack/v2 v2.3.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4=
github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
@@ -1304,8 +1306,6 @@ go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzK
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
@@ -1343,8 +1343,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.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
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=
@@ -1360,8 +1360,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.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA=
golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA=
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
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=
@@ -1386,8 +1386,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1441,8 +1441,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1450,8 +1450,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.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
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=
@@ -1554,8 +1554,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.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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=
@@ -1567,8 +1567,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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
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=
@@ -1583,8 +1583,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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
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=
@@ -1647,8 +1647,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -521,6 +521,24 @@ func trapShutdownCtx(s *Service, srv *http.Server, ctx context.Context) error {
s.Log.Debug().Msg("runtime listener shutdown done")
}()
// shutdown services in the order defined in the config
// any services not listed will be shutdown in parallel afterwards
for _, sName := range s.cfg.Runtime.ShutdownOrder {
if _, ok := s.serviceToken[sName]; !ok {
s.Log.Warn().Str("service", sName).Msg("unknown service for ordered shutdown, skipping")
continue
}
for i := range s.serviceToken[sName] {
if err := s.Supervisor.RemoveAndWait(s.serviceToken[sName][i], _defaultShutdownTimeoutDuration); err != nil && !errors.Is(err, suture.ErrSupervisorNotRunning) {
s.Log.Error().Err(err).Str("service", sName).Msg("could not shutdown service in order, skipping to next")
// continue shutting down other services
continue
}
s.Log.Debug().Str("service", sName).Msg("graceful ordered shutdown for service done")
}
delete(s.serviceToken, sName)
}
for sName := range s.serviceToken {
for i := range s.serviceToken[sName] {
wg.Add(1)

View File

@@ -50,24 +50,26 @@ type Mode int
// Runtime configures the OpenCloud runtime when running in supervised mode.
type Runtime struct {
Port string `yaml:"port" env:"OC_RUNTIME_PORT" desc:"The TCP port at which OpenCloud will be available" introductionVersion:"1.0.0"`
Host string `yaml:"host" env:"OC_RUNTIME_HOST" desc:"The host at which OpenCloud will be available" introductionVersion:"1.0.0"`
Services []string `yaml:"services" env:"OC_RUN_EXTENSIONS;OC_RUN_SERVICES" desc:"A comma-separated list of service names. Will start only the listed services." introductionVersion:"1.0.0"`
Disabled []string `yaml:"disabled_services" env:"OC_EXCLUDE_RUN_SERVICES" desc:"A comma-separated list of service names. Will start all default services except of the ones listed. Has no effect when OC_RUN_SERVICES is set." introductionVersion:"1.0.0"`
Additional []string `yaml:"add_services" env:"OC_ADD_RUN_SERVICES" desc:"A comma-separated list of service names. Will add the listed services to the default configuration. Has no effect when OC_RUN_SERVICES is set. Note that one can add services not started by the default list and exclude services from the default list by using both envvars at the same time." introductionVersion:"1.0.0"`
Port string `yaml:"port" env:"OC_RUNTIME_PORT" desc:"The TCP port at which OpenCloud will be available" introductionVersion:"1.0.0"`
Host string `yaml:"host" env:"OC_RUNTIME_HOST" desc:"The host at which OpenCloud will be available" introductionVersion:"1.0.0"`
Services []string `yaml:"services" env:"OC_RUN_EXTENSIONS;OC_RUN_SERVICES" desc:"A comma-separated list of service names. Will start only the listed services." introductionVersion:"1.0.0"`
Disabled []string `yaml:"disabled_services" env:"OC_EXCLUDE_RUN_SERVICES" desc:"A comma-separated list of service names. Will start all default services except of the ones listed. Has no effect when OC_RUN_SERVICES is set." introductionVersion:"1.0.0"`
Additional []string `yaml:"add_services" env:"OC_ADD_RUN_SERVICES" desc:"A comma-separated list of service names. Will add the listed services to the default configuration. Has no effect when OC_RUN_SERVICES is set. Note that one can add services not started by the default list and exclude services from the default list by using both envvars at the same time." introductionVersion:"1.0.0"`
ShutdownOrder []string `yaml:"shutdown_order" env:"OC_SHUTDOWN_ORDER" desc:"A comma-separated list of service names defining the order in which services are shut down. Services not listed will be stopped after the listed ones in random order." introductionVersion:"%%NEXT%%"`
}
// Config combines all available configuration parts.
type Config struct {
*shared.Commons `yaml:"shared"`
Tracing *shared.Tracing `yaml:"tracing"`
Log *shared.Log `yaml:"log"`
Cache *shared.Cache `yaml:"cache"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *shared.GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS shared.HTTPServiceTLS `yaml:"http_service_tls"`
Reva *shared.Reva `yaml:"reva"`
Tracing *shared.Tracing `yaml:"tracing"`
Log *shared.Log `yaml:"log"`
Cache *shared.Cache `yaml:"cache"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *shared.GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS shared.HTTPServiceTLS `yaml:"http_service_tls"`
HTTPServiceTimeout shared.HTTPServiceTimeout `yaml:"http_service_timeout"`
Reva *shared.Reva `yaml:"reva"`
Mode Mode // DEPRECATED
File string

View File

@@ -50,8 +50,9 @@ func DefaultConfig() *Config {
return &Config{
OpenCloudURL: "https://localhost:9200",
Runtime: Runtime{
Port: "9250",
Host: "localhost",
Port: "9250",
Host: "localhost",
ShutdownOrder: []string{"proxy"},
},
Reva: &shared.Reva{
Address: "eu.opencloud.api.gateway",

View File

@@ -2,6 +2,7 @@ package parser
import (
"errors"
"time"
"github.com/opencloud-eu/opencloud/pkg/config"
"github.com/opencloud-eu/opencloud/pkg/config/envdecode"
@@ -61,6 +62,8 @@ func EnsureDefaults(cfg *config.Config) {
if cfg.Reva == nil {
cfg.Reva = &shared.Reva{}
}
cfg.HTTPServiceTimeout.Read = 60 * time.Second
}
// EnsureCommons copies applicable parts of the OpenCloud config into the commons part
@@ -83,6 +86,7 @@ func EnsureCommons(cfg *config.Config) {
}
cfg.Commons.HTTPServiceTLS = cfg.HTTPServiceTLS
cfg.Commons.HTTPServiceTimeout = cfg.HTTPServiceTimeout
cfg.Commons.TokenManager = structs.CopyOrZeroValue(cfg.TokenManager)

View File

@@ -4,10 +4,11 @@ import (
"context"
"net/http"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/urfave/cli/v2"
"go.opentelemetry.io/otel/trace"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/shared"
)
// Option defines a single option function.
@@ -17,6 +18,7 @@ type Option func(o *Options)
type Options struct {
Logger log.Logger
TLSConfig shared.HTTPServiceTLS
TimeoutConfig shared.HTTPServiceTimeout
Namespace string
Name string
Version string
@@ -96,6 +98,13 @@ func TLSConfig(config shared.HTTPServiceTLS) Option {
}
}
// TimeoutConfig provides a function to set the TimeOutConfig option.
func TimeoutConfig(config shared.HTTPServiceTimeout) Option {
return func(o *Options) {
o.TimeoutConfig = config
}
}
// TraceProvider provides a function to set the TraceProvider option.
func TraceProvider(tp trace.TracerProvider) Option {
return func(o *Options) {

View File

@@ -3,16 +3,18 @@ package http
import (
"crypto/tls"
"fmt"
"net"
"strings"
"github.com/opencloud-eu/opencloud/pkg/broker"
"github.com/opencloud-eu/opencloud/pkg/registry"
netx "github.com/opencloud-eu/opencloud/pkg/x/net"
mhttps "github.com/go-micro/plugins/v4/server/http"
mtracer "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry"
occrypto "github.com/opencloud-eu/opencloud/pkg/crypto"
"go-micro.dev/v4"
"go-micro.dev/v4/server"
occrypto "github.com/opencloud-eu/opencloud/pkg/crypto"
)
// Service simply wraps the go-micro web service.
@@ -24,7 +26,9 @@ type Service struct {
func NewService(opts ...Option) (Service, error) {
noopBroker := broker.NoOp{}
sopts := newOptions(opts...)
var mServer server.Server
var listener net.Listener
var err error
if sopts.TLSConfig.Enabled {
var cert tls.Certificate
var err error
@@ -50,11 +54,27 @@ func NewService(opts ...Option) (Service, error) {
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
}
mServer = mhttps.NewServer(server.TLSConfig(tlsConfig))
// Create TLS listener
listener, err = tls.Listen("tcp", sopts.Address, tlsConfig)
if err != nil {
return Service{}, fmt.Errorf("error starting TLS listener: %w", err)
}
} else {
mServer = mhttps.NewServer()
// Create Non-TLS listener
listener, err = net.Listen("tcp", sopts.Address)
if err != nil {
return Service{}, fmt.Errorf("error starting TCP listener: %w", err)
}
}
mServer := mhttps.NewServer(
// Wrap listener with timeoutListener to set a read timeout
mhttps.Listener(netx.TimeoutListener{
Listener: listener,
ReadTimeout: sopts.TimeoutConfig.Read,
}),
)
wopts := []micro.Option{
micro.Server(mServer),
micro.Broker(noopBroker),

View File

@@ -55,6 +55,10 @@ type HTTPServiceTLS struct {
Key string `yaml:"key" env:"OC_HTTP_TLS_KEY" desc:"Path/File name for the TLS certificate key (in PEM format) for the server certificate to use for the http services." introductionVersion:"1.0.0"`
}
type HTTPServiceTimeout struct {
Read time.Duration `yaml:"duration" env:"OC_HTTP_TIMEOUT_READ" desc:"The duration after which a read operation will time out." introductionVersion:"%%NEXT%%"`
}
type Cache struct {
Store string `yaml:"store" env:"OC_CACHE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'redis-sentinel', 'nats-js-kv', 'noop'. See the text description for details." introductionVersion:"1.0.0"`
Nodes []string `yaml:"nodes" env:"OC_CACHE_STORE_NODES" desc:"A comma separated list of nodes to access the configured store. This has no effect when 'memory' store is configured. Note that the behaviour how nodes are used is dependent on the library of the configured store." introductionVersion:"1.0.0"`
@@ -69,21 +73,22 @@ type Cache struct {
// Commons holds configuration that are common to all extensions. Each extension can then decide whether
// to overwrite its values.
type Commons struct {
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
Cache *Cache `yaml:"cache"`
GRPCClientTLS *GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS HTTPServiceTLS `yaml:"http_service_tls"`
OpenCloudURL string `yaml:"opencloud_url" env:"OC_URL" desc:"URL, where OpenCloud is reachable for users." introductionVersion:"1.0.0"`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OC_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services." introductionVersion:"1.0.0"`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET" desc:"The secret used for signing the requests towards the data gateway for up- and downloads." introductionVersion:"1.0.0"`
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID" desc:"ID of the OpenCloud storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"1.0.0"`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY" desc:"API key for all system users." introductionVersion:"1.0.0"`
AdminUserID string `yaml:"admin_user_id" env:"OC_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges. Consider that the UUID can be encoded in some LDAP deployment configurations like in .ldif files. These need to be decoded beforehand." introductionVersion:"1.0.0"`
MultiTenantEnabled bool `yaml:"multi_tenant_enabled" env:"OC_MULTI_TENANT_ENABLED" desc:"Set this to true to enable multi-tenant support." introductionVersion:"%%NEXT%%"`
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
Cache *Cache `yaml:"cache"`
GRPCClientTLS *GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS HTTPServiceTLS `yaml:"http_service_tls"`
HTTPServiceTimeout HTTPServiceTimeout `yaml:"http_service_timeout"`
OpenCloudURL string `yaml:"opencloud_url" env:"OC_URL" desc:"URL, where OpenCloud is reachable for users." introductionVersion:"1.0.0"`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OC_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services." introductionVersion:"1.0.0"`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET" desc:"The secret used for signing the requests towards the data gateway for up- and downloads." introductionVersion:"1.0.0"`
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID" desc:"ID of the OpenCloud storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"1.0.0"`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY" desc:"API key for all system users." introductionVersion:"1.0.0"`
AdminUserID string `yaml:"admin_user_id" env:"OC_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges. Consider that the UUID can be encoded in some LDAP deployment configurations like in .ldif files. These need to be decoded beforehand." introductionVersion:"1.0.0"`
MultiTenantEnabled bool `yaml:"multi_tenant_enabled" env:"OC_MULTI_TENANT_ENABLED" desc:"Set this to true to enable multi-tenant support." introductionVersion:"%%NEXT%%"`
// NOTE: you will not fing GRPCMaxReceivedMessageSize size being used in the code. The envvar is actually extracted in revas `pool` package: https://github.com/cs3org/reva/blob/edge/pkg/rgrpc/todo/pool/connection.go
// It is mentioned here again so it is documented

View File

@@ -16,7 +16,7 @@ var (
// LatestTag is the latest released version plus the dev meta version.
// Will be overwritten by the release pipeline
// Needs a manual change for every tagged release
LatestTag = "3.5.0+dev"
LatestTag = "3.7.0+dev"
// Date indicates the build date.
// This has been removed, it looks like you can only replace static strings with recent go versions

47
pkg/x/net/listener.go Normal file
View File

@@ -0,0 +1,47 @@
package net
import (
"io"
gonet "net"
"time"
)
type TimeoutListener struct {
gonet.Listener
ReadTimeout time.Duration
}
func (l TimeoutListener) Accept() (gonet.Conn, error) {
c, err := l.Listener.Accept()
if err != nil {
return nil, err
}
return &TimeoutConn{Conn: c, readTimeout: l.ReadTimeout}, nil
}
type TimeoutConn struct {
gonet.Conn
readTimeout time.Duration
bodyDone bool
}
// Read implements a read with a sliding timeout window.
func (c *TimeoutConn) Read(b []byte) (int, error) {
if c.readTimeout > 0 && !c.bodyDone {
if err := c.SetReadDeadline(time.Now().Add(c.readTimeout)); err != nil {
return 0, err
}
}
n, err := c.Conn.Read(b)
if n > 0 && c.readTimeout > 0 && !c.bodyDone {
// reset deadline after every successful read
_ = c.SetReadDeadline(time.Now().Add(c.readTimeout))
}
if err == io.EOF {
c.bodyDone = true
}
return n, err
}

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Ivan Fustero, 2025\n"
"Language-Team: Catalan (https://app.transifex.com/opencloud-eu/teams/204053/ca/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Jörn Friedrich Dreyer <jfd@butonic.de>, 2025\n"
"Language-Team: German (https://app.transifex.com/opencloud-eu/teams/204053/de/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Elías Martín, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Simone Broglia, 2025\n"
"Language-Team: Italian (https://app.transifex.com/opencloud-eu/teams/204053/it/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Junghyuk Kwon <kwon@junghy.uk>, 2025\n"
"Language-Team: Korean (https://app.transifex.com/opencloud-eu/teams/204053/ko/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-02 00:02+0000\n"
"POT-Creation-Date: 2025-10-23 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-02 00:02+0000\n"
"POT-Creation-Date: 2025-10-23 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Lulufox, 2025\n"
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: YQS Yang, 2025\n"
"Language-Team: Chinese (https://app.transifex.com/opencloud-eu/teams/204053/zh/)\n"

View File

@@ -286,11 +286,6 @@ func (s *Service) addQueryToURL(baseURL string, req *appproviderv1beta1.OpenInAp
if strings.ToLower(s.config.App.Product) == "collabora" {
q.Add("closebutton", "false")
cssVariables := utils.ReadPlainFromOpaque(req.GetOpaque(), "cssVariables")
if cssVariables != "" {
q.Add("css_variables", cssVariables)
}
}
qs := q.Encode()

View File

@@ -62,6 +62,8 @@ type Config struct {
ConfigurableNotifications bool `yaml:"configurable_notifications" env:"FRONTEND_CONFIGURABLE_NOTIFICATIONS" desc:"Allow configuring notifications via web client." introductionVersion:"1.0.0"`
Groupware Groupware `yaml:"groupware"`
Context context.Context `yaml:"-"`
}
@@ -195,3 +197,7 @@ type PasswordPolicy struct {
MinSpecialCharacters int `yaml:"min_special_characters" env:"OC_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 1 if not set." introductionVersion:"1.0.0"`
BannedPasswordsList string `yaml:"banned_passwords_list" env:"OC_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. This only impacts public link password validation. See the documentation for more details." introductionVersion:"1.0.0"`
}
type Groupware struct {
Enabled bool `yaml:"enabled" env:"FRONTEND_GROUPWARE_ENABLED" desc:"Enable groupware features. Defaults to false." introductionVersion:"3.7.0"`
}

View File

@@ -139,6 +139,9 @@ func DefaultConfig() *config.Config {
MinDigits: 1,
MinSpecialCharacters: 1,
},
Groupware: config.Groupware{
Enabled: false,
},
}
}

View File

@@ -339,6 +339,9 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
"endpoints": []string{"list", "get", "delete"},
"configurable": cfg.ConfigurableNotifications,
},
"groupware": map[string]interface{}{
"enabled": cfg.Groupware.Enabled,
},
},
"version": map[string]interface{}{
"product": "OpenCloud",

View File

@@ -48,36 +48,37 @@ func Server(cfg *config.Config) *cli.Command {
mtrcs := metrics.New()
mtrcs.BuildInfo.WithLabelValues(version.GetString()).Set(1)
//Connect to NATS servers
natsOptions := nats.Options{
Servers: cfg.Store.Nodes,
User: cfg.Store.AuthUsername,
Password: cfg.Store.AuthPassword,
}
conn, err := natsOptions.Connect()
if err != nil {
return err
}
js, err := jetstream.New(conn)
if err != nil {
return err
}
kv, err := js.KeyValue(ctx, cfg.Store.Database)
if err != nil {
if !errors.Is(err, jetstream.ErrBucketNotFound) {
return fmt.Errorf("failed to get bucket (%s): %w", cfg.Store.Database, err)
var kv jetstream.KeyValue
// Allow to run without a NATS store (e.g. for the standalone Education provisioning service)
if len(cfg.Store.Nodes) > 0 {
//Connect to NATS servers
natsOptions := nats.Options{
Servers: cfg.Store.Nodes,
User: cfg.Store.AuthUsername,
Password: cfg.Store.AuthPassword,
}
kv, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{
Bucket: cfg.Store.Database,
})
conn, err := natsOptions.Connect()
if err != nil {
return fmt.Errorf("failed to create bucket (%s): %w", cfg.Store.Database, err)
return err
}
js, err := jetstream.New(conn)
if err != nil {
return err
}
kv, err = js.KeyValue(ctx, cfg.Store.Database)
if err != nil {
if !errors.Is(err, jetstream.ErrBucketNotFound) {
return fmt.Errorf("failed to get bucket (%s): %w", cfg.Store.Database, err)
}
kv, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{
Bucket: cfg.Store.Database,
})
if err != nil {
return fmt.Errorf("failed to create bucket (%s): %w", cfg.Store.Database, err)
}
}
}
if err != nil {
return err
}
gr := runner.NewGroup()

View File

@@ -8,10 +8,10 @@ import (
"github.com/go-ldap/ldap/v3"
"github.com/gofrs/uuid"
libregraph "github.com/opencloud-eu/libre-graph-api-go"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
libregraph "github.com/opencloud-eu/libre-graph-api-go"
)
type educationConfig struct {
@@ -119,16 +119,18 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ
}
// Check that the school number is not already used
_, err := i.getSchoolByNumber(school.GetSchoolNumber())
switch err {
case nil:
logger.Debug().Err(errSchoolNumberExists).Str("schoolNumber", school.GetSchoolNumber()).Msg("duplicate school number")
return nil, errSchoolNumberExists
case ErrNotFound:
break
default:
logger.Error().Err(err).Str("schoolNumber", school.GetSchoolNumber()).Msg("error looking up school by number")
return nil, errorcode.New(errorcode.GeneralException, "error looking up school by number")
if school.HasSchoolNumber() {
_, err := i.getSchoolByNumber(school.GetSchoolNumber())
switch err {
case nil:
logger.Debug().Err(errSchoolNumberExists).Str("schoolNumber", school.GetSchoolNumber()).Msg("duplicate school number")
return nil, errSchoolNumberExists
case ErrNotFound:
break
default:
logger.Error().Err(err).Str("schoolNumber", school.GetSchoolNumber()).Msg("error looking up school by number")
return nil, errorcode.New(errorcode.GeneralException, "error looking up school by number")
}
}
attributeTypeAndValue := ldap.AttributeTypeAndValue{
@@ -142,7 +144,9 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ
)
ar := ldap.NewAddRequest(dn, nil)
ar.Attribute(i.educationConfig.schoolAttributeMap.displayName, []string{school.GetDisplayName()})
ar.Attribute(i.educationConfig.schoolAttributeMap.schoolNumber, []string{school.GetSchoolNumber()})
if school.HasSchoolNumber() {
ar.Attribute(i.educationConfig.schoolAttributeMap.schoolNumber, []string{school.GetSchoolNumber()})
}
if !i.useServerUUID {
ar.Attribute(i.educationConfig.schoolAttributeMap.id, []string{uuid.Must(uuid.NewV4()).String()})
}
@@ -723,18 +727,22 @@ func (i *LDAP) createSchoolModelFromLDAP(e *ldap.Entry) *libregraph.EducationSch
if err != nil && !errors.Is(err, errNotSet) {
i.logger.Error().Err(err).Str("dn", e.DN).Msg("Error reading termination date for LDAP entry")
}
if id != "" && displayName != "" && schoolNumber != "" {
school := libregraph.NewEducationSchool()
school.SetDisplayName(displayName)
school.SetSchoolNumber(schoolNumber)
school.SetId(id)
if t != nil {
school.SetTerminationDate(*t)
}
return school
if id == "" || displayName == "" {
i.logger.Warn().Str("dn", e.DN).Str("id", id).Str("displayName", displayName).Msg("Invalid School. Missing required attribute")
return nil
}
i.logger.Warn().Str("dn", e.DN).Str("id", id).Str("displayName", displayName).Str("schoolNumber", schoolNumber).Msg("Invalid School. Missing required attribute")
return nil
school := libregraph.NewEducationSchool()
school.SetDisplayName(displayName)
school.SetId(id)
if schoolNumber != "" {
school.SetSchoolNumber(schoolNumber)
}
if t != nil {
school.SetTerminationDate(*t)
}
return school
}
func (i *LDAP) getSchoolNumber(e *ldap.Entry) string {

View File

@@ -83,9 +83,8 @@ func (i *LDAP) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*li
if search != "" {
search = ldap.EscapeFilter(search)
groupFilter = fmt.Sprintf(
"(|(%s=*%s*)(%s=*%s*))",
"(%s=*%s*)",
i.groupAttributeMap.name, search,
i.groupAttributeMap.id, search,
)
}
groupFilter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.groupFilter, i.groupObjectClass, groupFilter)

View File

@@ -305,7 +305,7 @@ func TestGetGroupsSearch(t *testing.T) {
// only match if the filter contains the search term unquoted
lm.On("Search", mock.MatchedBy(
func(req *ldap.SearchRequest) bool {
return req.Filter == "(&(objectClass=groupOfNames)(|(cn=*term*)(entryUUID=*term*)))"
return req.Filter == "(&(objectClass=groupOfNames)(cn=*term*))"
})).
Return(&ldap.SearchResult{}, nil)
b, _ := getMockedBackend(lm, lconfig, &logger)

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Ivan Fustero, 2025\n"
"Language-Team: Catalan (https://app.transifex.com/opencloud-eu/teams/204053/ca/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Jörn Friedrich Dreyer <jfd@butonic.de>, 2025\n"
"Language-Team: German (https://app.transifex.com/opencloud-eu/teams/204053/de/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Elías Martín, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Simone Broglia, 2025\n"
"Language-Team: Italian (https://app.transifex.com/opencloud-eu/teams/204053/it/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: gapho shin, 2025\n"
"Language-Team: Korean (https://app.transifex.com/opencloud-eu/teams/204053/ko/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-05 00:01+0000\n"
"POT-Creation-Date: 2025-10-26 00:00+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-12 00:00+0000\n"
"POT-Creation-Date: 2025-11-01 00:02+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Lulufox, 2025\n"
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-26 00:01+0000\n"
"POT-Creation-Date: 2025-10-16 08:04+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: LinkinWires <darkinsonic13@gmail.com>, 2025\n"
"Language-Team: Ukrainian (https://app.transifex.com/opencloud-eu/teams/204053/uk/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: YQS Yang, 2025\n"
"Language-Team: Chinese (https://app.transifex.com/opencloud-eu/teams/204053/zh/)\n"

View File

@@ -18,13 +18,23 @@ func Server(opts ...Option) (*http.Server, error) {
WithLogger(options.Logger).
WithCheck("web reachability", checks.NewHTTPCheck(options.Config.HTTP.Addr))
u, err := url.Parse(options.Config.Identity.LDAP.URI)
if err != nil {
return nil, err
readyHandlerConfiguration := healthHandlerConfiguration
// Check for LDAP reachability, when we're using the LDAP backend
if options.Config.Identity.Backend == "ldap" {
u, err := url.Parse(options.Config.Identity.LDAP.URI)
if err != nil {
return nil, err
}
readyHandlerConfiguration = readyHandlerConfiguration.
WithCheck("ldap reachability", checks.NewTCPCheck(u.Host))
}
// only check nats if really needed
if options.Config.Events.Endpoint != "" {
readyHandlerConfiguration = readyHandlerConfiguration.
WithCheck("nats reachability", checks.NewNatsCheck(options.Config.Events.Endpoint))
}
readyHandlerConfiguration := healthHandlerConfiguration.
WithCheck("nats reachability", checks.NewNatsCheck(options.Config.Events.Endpoint)).
WithCheck("ldap reachability", checks.NewTCPCheck(u.Host))
return debug.NewService(
debug.Logger(options.Logger),

View File

@@ -74,12 +74,6 @@ func (g Graph) PostEducationSchool(w http.ResponseWriter, r *http.Request) {
return
}
if _, ok := school.GetSchoolNumberOk(); !ok {
logger.Debug().Interface("school", school).Msg("could not create school: missing required attribute")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Missing Required Attribute")
return
}
// validate terminationDate attribute, needs to be "far enough" in the future, terminationDate can be nil (means
// termination date is to be deleted
if terminationDate, ok := school.GetTerminationDateOk(); ok && terminationDate != nil {

View File

@@ -232,18 +232,6 @@ var _ = Describe("Schools", func() {
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("handles missing school number", func() {
newSchool = libregraph.NewEducationSchool()
newSchool.SetDisplayName("New School")
newSchoolJson, err := json.Marshal(newSchool)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
svc.PostEducationSchool(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("disallows school create ids", func() {
newSchool = libregraph.NewEducationSchool()
newSchool.SetId("disallowed")

View File

@@ -76,18 +76,7 @@ func (g Graph) PostEducationUser(w http.ResponseWriter, r *http.Request) {
return
}
identities, ok := u.GetIdentitiesOk()
if !ok {
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing required Collection: 'identities'")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'identities'")
return
}
if len(identities) < 1 {
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing entry in Collection: 'identities'")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Collection: 'identities'")
return
}
for i, identity := range identities {
for i, identity := range u.GetIdentities() {
if _, ok := identity.GetIssuerOk(); !ok {
logger.Debug().Err(err).Interface("user", u).Msgf("could not create education user: missing Attribute in 'identities' Collection Entry %d: 'issuer'", i)
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("missing Attribute in 'identities' Collection Entry %d: 'issuer'", i))
@@ -130,12 +119,6 @@ func (g Graph) PostEducationUser(w http.ResponseWriter, r *http.Request) {
u.SetUserType("Member")
}
if _, ok := u.GetPrimaryRoleOk(); !ok {
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing required Attribute: 'primaryRole'")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'primaryRole'")
return
}
logger.Debug().Interface("user", u).Msg("calling create education user on backend")
if u, err = g.identityEducationBackend.CreateEducationUser(r.Context(), *u); err != nil {
logger.Debug().Err(err).Msg("could not create education user: backend error")

View File

@@ -3,9 +3,12 @@ package channels
import (
"context"
"crypto/rand"
"crypto/tls"
"fmt"
stdmail "net/mail"
"strings"
"time"
"github.com/pkg/errors"
mail "github.com/xhit/go-simple-mail/v2"
@@ -118,6 +121,7 @@ func (m Mail) SendMessage(_ context.Context, message *Message) error {
email := mail.NewMSG()
email.SetFrom(appendSender(message.Sender, m.smtpAddress)).AddTo(message.Recipient...)
email.SetSubject(message.Subject)
email.AddHeader("Message-ID", generateMessageID(m.smtpAddress.Address))
email.SetBody(mail.TextPlain, message.TextBody)
if message.HTMLBody != "" {
email.AddAlternative(mail.TextHTML, message.HTMLBody)
@@ -135,3 +139,22 @@ func appendSender(sender string, a stdmail.Address) string {
}
return a.String()
}
// generateMessageID generates a unique Message-ID header value according to RFC 5322
func generateMessageID(domain string) string {
// Extract domain from email address if it contains @
if idx := strings.LastIndex(domain, "@"); idx != -1 {
domain = domain[idx+1:]
}
// Generate random bytes for uniqueness
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
// Fallback to timestamp-based ID if random fails
return fmt.Sprintf("<%d@%s>", time.Now().UnixNano(), domain)
}
// Create Message-ID: <timestamp.random@domain>
timestamp := time.Now().Unix()
return fmt.Sprintf("<%d.%x@%s>", timestamp, b, domain)
}

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Ivan Fustero, 2025\n"
"Language-Team: Catalan (https://app.transifex.com/opencloud-eu/teams/204053/ca/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Jonas, 2025\n"
"Language-Team: German (https://app.transifex.com/opencloud-eu/teams/204053/de/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-13 00:01+0000\n"
"POT-Creation-Date: 2025-11-02 00:02+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: miguel tapias, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Simone Broglia, 2025\n"
"Language-Team: Italian (https://app.transifex.com/opencloud-eu/teams/204053/it/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: gapho shin, 2025\n"
"Language-Team: Korean (https://app.transifex.com/opencloud-eu/teams/204053/ko/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-05 00:01+0000\n"
"POT-Creation-Date: 2025-10-26 00:00+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-07 00:00+0000\n"
"POT-Creation-Date: 2025-10-27 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Lulufox, 2025\n"
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Davis Kaza, 2025\n"
"Language-Team: Swedish (https://app.transifex.com/opencloud-eu/teams/204053/sv/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: YQS Yang, 2025\n"
"Language-Team: Chinese (https://app.transifex.com/opencloud-eu/teams/204053/zh/)\n"

View File

@@ -4,11 +4,12 @@ import (
"fmt"
"os"
"go-micro.dev/v4"
pkgcrypto "github.com/opencloud-eu/opencloud/pkg/crypto"
"github.com/opencloud-eu/opencloud/pkg/service/http"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/pkg/version"
"go-micro.dev/v4"
)
// Server initializes the http service and server.
@@ -40,6 +41,7 @@ func Server(opts ...Option) (http.Service, error) {
Cert: options.Config.HTTP.TLSCert,
Key: options.Config.HTTP.TLSKey,
}),
http.TimeoutConfig(options.Config.Commons.HTTPServiceTimeout),
http.Logger(options.Logger),
http.Address(options.Config.HTTP.Addr),
http.Namespace(options.Config.HTTP.Namespace),

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Ivan Fustero, 2025\n"
"Language-Team: Catalan (https://app.transifex.com/opencloud-eu/teams/204053/ca/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Jörn Friedrich Dreyer <jfd@butonic.de>, 2025\n"
"Language-Team: German (https://app.transifex.com/opencloud-eu/teams/204053/de/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Alejandro Robles, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"

View File

@@ -0,0 +1,150 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
# Translators:
# idoet <idoet@protonmail.ch>, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-29 00:02+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: idoet <idoet@protonmail.ch>, 2025\n"
"Language-Team: Indonesian (https://app.transifex.com/opencloud-eu/teams/204053/id/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: id\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. name of the notification option 'Space Shared'
#: pkg/store/defaults/templates.go:20
msgid "Added as space member"
msgstr "Ditambahkan menjadi anggota ruang penyimpanan"
#. translation for the 'daily' email interval option
#: pkg/store/defaults/templates.go:50
msgid "Daily"
msgstr "Harian"
#. name of the notification option 'Email Interval'
#: pkg/store/defaults/templates.go:44
msgid "Email sending interval"
msgstr "Interval pengiriman email"
#. name of the notification option 'File Rejected'
#: pkg/store/defaults/templates.go:40
msgid "File rejected"
msgstr "Berkas ditolak"
#. translation for the 'instant' email interval option
#: pkg/store/defaults/templates.go:48
msgid "Instant"
msgstr "Instan"
#. translation for the 'never' email interval option
#: pkg/store/defaults/templates.go:54
msgid "Never"
msgstr "Tidak pernah"
#. description of the notification option 'Space Shared'
#: pkg/store/defaults/templates.go:22
msgid "Notify when I have been added as a member to a space"
msgstr ""
"Beri tahu ketika saya telah ditambahkan menjadi anggota dari suatu ruang "
"penyimpanan"
#. description of the notification option 'Space Unshared'
#: pkg/store/defaults/templates.go:26
msgid "Notify when I have been removed as member from a space"
msgstr ""
"Beri tahu ketika saya telah dihapus dari keanggotaan suatu ruang penyimpanan"
#. description of the notification option 'Share Received'
#: pkg/store/defaults/templates.go:10
msgid "Notify when I have received a share"
msgstr "Beri tahu ketika saya telah menerima berbagi"
#. description of the notification option 'File Rejected'
#: pkg/store/defaults/templates.go:42
msgid ""
"Notify when a file I uploaded was rejected because of a virus infection or "
"policy violation"
msgstr ""
"Beri tahu jika file yang saya unggah ditolak karena infeksi virus atau "
"pelanggaran kebijakan"
#. description of the notification option 'Share Removed'
#: pkg/store/defaults/templates.go:14
msgid "Notify when a received share has been removed"
msgstr "Beri tahu saat berbagi yang diterima telah dihapus"
#. description of the notification option 'Share Expired'
#: pkg/store/defaults/templates.go:18
msgid "Notify when a received share has expired"
msgstr "Beri tahu ketika berbagi yang diterima telah berakhir"
#. description of the notification option 'Space Deleted'
#: pkg/store/defaults/templates.go:38
msgid "Notify when a space I am member of has been deleted"
msgstr "Beri tahu ketika ruang penyimpanan yang saya ikuti telah dihapus"
#. description of the notification option 'Space Disabled'
#: pkg/store/defaults/templates.go:34
msgid "Notify when a space I am member of has been disabled"
msgstr ""
"Beri tahu ketika ruang penyimpanan yang saya ikuti telah dinonaktifkan"
#. description of the notification option 'Space Membership Expired'
#: pkg/store/defaults/templates.go:30
msgid "Notify when a space membership has expired"
msgstr "Beri tahu saat keanggotaan ruang penyimpanan telah berakhir"
#. name of the notification option 'Space Unshared'
#: pkg/store/defaults/templates.go:24
msgid "Removed as space member"
msgstr "Dihapus dari anggota ruang penyimpanan"
#. description of the notification option 'Email Interval'
#: pkg/store/defaults/templates.go:46
msgid "Selected value:"
msgstr "Yang dipilih:"
#. name of the notification option 'Share Expired'
#: pkg/store/defaults/templates.go:16
msgid "Share Expired"
msgstr "Berbagi Berakhir"
#. name of the notification option 'Share Received'
#: pkg/store/defaults/templates.go:8
msgid "Share Received"
msgstr "Berbagi Diterima"
#. name of the notification option 'Share Removed'
#: pkg/store/defaults/templates.go:12
msgid "Share Removed"
msgstr "Berbagi Dihapus"
#. name of the notification option 'Space Deleted'
#: pkg/store/defaults/templates.go:36
msgid "Space deleted"
msgstr "Ruang penyimpanan dihapus"
#. name of the notification option 'Space Disabled'
#: pkg/store/defaults/templates.go:32
msgid "Space disabled"
msgstr "Ruang penyimpanan dinonaktifkan"
#. name of the notification option 'Space Membership Expired'
#: pkg/store/defaults/templates.go:28
msgid "Space membership expired"
msgstr "Keanggotaan ruang penyimpanan telah berakhir"
#. translation for the 'weekly' email interval option
#: pkg/store/defaults/templates.go:52
msgid "Weekly"
msgstr "Mingguan"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Simone Pagano, 2025\n"
"Language-Team: Italian (https://app.transifex.com/opencloud-eu/teams/204053/it/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: gapho shin, 2025\n"
"Language-Team: Korean (https://app.transifex.com/opencloud-eu/teams/204053/ko/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-02 00:02+0000\n"
"POT-Creation-Date: 2025-10-23 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Lulufox, 2025\n"
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Davis Kaza, 2025\n"
"Language-Team: Swedish (https://app.transifex.com/opencloud-eu/teams/204053/sv/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-26 00:01+0000\n"
"POT-Creation-Date: 2025-10-16 08:04+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: LinkinWires <darkinsonic13@gmail.com>, 2025\n"
"Language-Team: Ukrainian (https://app.transifex.com/opencloud-eu/teams/204053/uk/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: YQS Yang, 2025\n"
"Language-Team: Chinese (https://app.transifex.com/opencloud-eu/teams/204053/zh/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Ivan Fustero, 2025\n"
"Language-Team: Catalan (https://app.transifex.com/opencloud-eu/teams/204053/ca/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Jörn Friedrich Dreyer <jfd@butonic.de>, 2025\n"
"Language-Team: German (https://app.transifex.com/opencloud-eu/teams/204053/de/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Elías Martín, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Simone Broglia, 2025\n"
"Language-Team: Italian (https://app.transifex.com/opencloud-eu/teams/204053/it/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: gapho shin, 2025\n"
"Language-Team: Korean (https://app.transifex.com/opencloud-eu/teams/204053/ko/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-10-05 00:01+0000\n"
"POT-Creation-Date: 2025-10-26 00:00+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: Lulufox, 2025\n"
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"

View File

@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-09-30 00:01+0000\n"
"POT-Creation-Date: 2025-10-20 00:01+0000\n"
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
"Last-Translator: YQS Yang, 2025\n"
"Language-Team: Chinese (https://app.transifex.com/opencloud-eu/teams/204053/zh/)\n"

View File

@@ -1,6 +1,6 @@
SHELL := bash
NAME := web
WEB_ASSETS_VERSION = v4.0.0
WEB_ASSETS_VERSION = v4.2.0
WEB_ASSETS_BRANCH = main
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI

View File

@@ -197,34 +197,6 @@ class GraphHelper {
return $baseUrl . '/graph/v1beta1/' . $path;
}
/**
* @param string $baseUrl
* @param string $xRequestId
* @param string $method
* @param string $path
* @param string|null $body
* @param array|null $headers
*
* @return RequestInterface
*/
public static function createRequest(
string $baseUrl,
string $xRequestId,
string $method,
string $path,
?string $body = null,
?array $headers = []
): RequestInterface {
$fullUrl = self::getFullUrl($baseUrl, $path);
return HttpRequestHelper::createRequest(
$fullUrl,
$xRequestId,
$method,
$headers,
$body
);
}
/**
* @param string $baseUrl
* @param string $xRequestId
@@ -1908,7 +1880,7 @@ class GraphHelper {
string $permissionsId
): ResponseInterface {
$url = self::getBetaFullUrl($baseUrl, "drives/$spaceId/items/$itemId/permissions/$permissionsId");
return HttpRequestHelper::sendRequestOnce(
return HttpRequestHelper::sendRequest(
$url,
$xRequestId,
'PATCH',
@@ -2264,7 +2236,7 @@ class GraphHelper {
): ResponseInterface {
$url = self::getBetaFullUrl($baseUrl, "drives/$spaceId/root/permissions/$permissionsId");
return HttpRequestHelper::sendRequestOnce(
return HttpRequestHelper::sendRequest(
$url,
$xRequestId,
'PATCH',

View File

@@ -74,6 +74,7 @@ class HttpRequestHelper {
* than download it all up-front.
* @param int|null $timeout
* @param Client|null $client
* @param string|null $bearerToken
*
* @return ResponseInterface
* @throws GuzzleException
@@ -90,7 +91,8 @@ class HttpRequestHelper {
?CookieJar $cookies = null,
bool $stream = false,
?int $timeout = 0,
?Client $client = null
?Client $client = null,
?string $bearerToken = null
): ResponseInterface {
if ($client === null) {
$client = self::createClient(
@@ -99,7 +101,8 @@ class HttpRequestHelper {
$config,
$cookies,
$stream,
$timeout
$timeout,
$bearerToken
);
}
@@ -200,6 +203,13 @@ class HttpRequestHelper {
} else {
$debugResponses = false;
}
// use basic auth for 'public' user or no user
if ($user === 'public' || $user === null || $user === '') {
$bearerToken = null;
} else {
$useBearerToken = TokenHelper::useBearerToken();
$bearerToken = $useBearerToken ? TokenHelper::getTokens($user, $password, $url)['access_token'] : null;
}
$sendRetryLimit = self::numRetriesOnHttpTooEarly();
$sendCount = 0;
@@ -217,7 +227,8 @@ class HttpRequestHelper {
$cookies,
$stream,
$timeout,
$client
$client,
$bearerToken,
);
if ($response->getStatusCode() >= 400
@@ -348,6 +359,7 @@ class HttpRequestHelper {
* @param bool $stream Set to true to stream a response rather
* than download it all up-front.
* @param int|null $timeout
* @param string|null $bearerToken
*
* @return Client
*/
@@ -357,10 +369,13 @@ class HttpRequestHelper {
?array $config = null,
?CookieJar $cookies = null,
?bool $stream = false,
?int $timeout = 0
?int $timeout = 0,
?string $bearerToken = null
): Client {
$options = [];
if ($user !== null) {
if ($bearerToken !== null) {
$options['headers']['Authorization'] = 'Bearer ' . $bearerToken;
} elseif ($user !== null) {
$options['auth'] = [$user, $password];
}
if ($config !== null) {

View File

@@ -0,0 +1,403 @@
<?php
/**
* @author Viktor Scharf <v.scharf@opencloud.eu>
* @copyright Copyright (c) 2025 Viktor Scharf <v.scharf@opencloud.eu>
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace TestHelpers;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\GuzzleException;
use Exception;
/**
* Helper for obtaining bearer tokens for users
*/
class TokenHelper {
private const LOGON_URL = '/signin/v1/identifier/_/logon';
private const REDIRECT_URL = '/oidc-callback.html';
private const TOKEN_URL = '/konnect/v1/token';
// Static cache [username => token_data]
private static array $tokenCache = [];
/**
* @return bool
*/
public static function useBearerToken(): bool {
return \getenv('USE_BEARER_TOKEN') === 'true';
}
/**
* Extracts base URL from a full URL
*
* @param string $url
*
* @return string the base URL
*/
private static function extractBaseUrl(string $url): string {
return preg_replace('#(https?://[^/]+).*#', '$1', $url);
}
/**
* Get access and refresh tokens for a user
* Uses cache to avoid unnecessary requests
*
* @param string $username
* @param string $password
* @param string $url
*
* @return array ['access_token' => string, 'refresh_token' => string, 'expires_at' => int]
* @throws GuzzleException
* @throws Exception
*/
public static function getTokens(string $username, string $password, string $url): array {
// Extract base URL. I need to send $url to get correct server in case of multiple servers (ocm suite)
$baseUrl = self::extractBaseUrl($url);
$cacheKey = $username . '|' . $baseUrl;
// Check cache
if (isset(self::$tokenCache[$cacheKey])) {
$cachedToken = self::$tokenCache[$cacheKey];
// Check if access token has expired
if (time() < $cachedToken['expires_at']) {
return $cachedToken;
}
$refreshedToken = self::refreshToken($cachedToken['refresh_token'], $baseUrl);
$tokenData = [
'access_token' => $refreshedToken['access_token'],
'refresh_token' => $refreshedToken['refresh_token'],
'expires_at' => time() + 300 // 5 minutes
];
self::$tokenCache[$cacheKey] = $tokenData;
return $tokenData;
}
// Get new tokens
$cookieJar = new CookieJar();
$continueUrl = self::getAuthorizedEndPoint($username, $password, $baseUrl, $cookieJar);
$code = self::getCode($continueUrl, $baseUrl, $cookieJar);
$tokens = self::getToken($code, $baseUrl, $cookieJar);
$tokenData = [
'access_token' => $tokens['access_token'],
'refresh_token' => $tokens['refresh_token'],
'expires_at' => time() + 290 // set expiry to 290 seconds to allow for some buffer
];
// Save to cache
self::$tokenCache[$cacheKey] = $tokenData;
return $tokenData;
}
/**
* Refresh token
*
* @param string $refreshToken
* @param string $baseUrl
*
* @return array
* @throws GuzzleException
* @throws Exception
*/
private static function refreshToken(string $refreshToken, string $baseUrl): array {
$client = new Client(
[
'verify' => false,
'http_errors' => false,
'allow_redirects' => false
]
);
$response = $client->post(
$baseUrl . self::TOKEN_URL,
[
'form_params' => [
'client_id' => 'web',
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
]
]
);
if ($response->getStatusCode() !== 200) {
throw new Exception(
\sprintf(
'Token refresh failed: Expected status code 200 but received %d. Message: %s',
$response->getStatusCode(),
$response->getReasonPhrase()
)
);
}
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['access_token']) || !isset($data['refresh_token'])) {
throw new Exception('Missing tokens in refresh response');
}
return [
'access_token' => $data['access_token'],
'refresh_token' => $data['refresh_token']
];
}
/**
* Clear cached tokens for a specific user
*
* @param string $username
* @param string $url
*
* @return void
*/
public static function clearUserTokens(string $username, string $url): void {
$baseUrl = self::extractBaseUrl($url);
$cacheKey = $username . '|' . $baseUrl;
unset(self::$tokenCache[$cacheKey]);
}
/**
* Clear all cached tokens
*
* @return void
*/
public static function clearAllTokens(): void {
self::$tokenCache = [];
}
/**
* @param string $username
* @param string $password
* @param string $baseUrl
* @param CookieJar $cookieJar
*
* @return \Psr\Http\Message\ResponseInterface
* @throws GuzzleException
*/
public static function makeLoginRequest(
string $username,
string $password,
string $baseUrl,
CookieJar $cookieJar
): \Psr\Http\Message\ResponseInterface {
$client = new Client(
[
'verify' => false,
'http_errors' => false,
'allow_redirects' => false,
'cookies' => $cookieJar
]
);
return $client->post(
$baseUrl . self::LOGON_URL,
[
'headers' => [
'Kopano-Konnect-XSRF' => '1',
'Referer' => $baseUrl,
'Content-Type' => 'application/json'
],
'json' => [
'params' => [$username, $password, '1'],
'hello' => [
'scope' => 'openid profile offline_access email',
'client_id' => 'web',
'redirect_uri' => $baseUrl . self::REDIRECT_URL,
'flow' => 'oidc'
]
]
]
);
}
/**
* Step 1: Login and get continue_uri
*
* @param string $username
* @param string $password
* @param string $baseUrl
* @param CookieJar $cookieJar
*
* @return string
* @throws GuzzleException
* @throws Exception
*/
private static function getAuthorizedEndPoint(
string $username,
string $password,
string $baseUrl,
CookieJar $cookieJar
): string {
$response = self::makeLoginRequest($username, $password, $baseUrl, $cookieJar);
if ($response->getStatusCode() !== 200) {
throw new Exception(
\sprintf(
'Logon failed: Expected status code 200 but received %d. Message: %s',
$response->getStatusCode(),
$response->getReasonPhrase()
)
);
}
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['hello']['continue_uri'])) {
throw new Exception('Missing continue_uri in logon response');
}
return $data['hello']['continue_uri'];
}
/**
* Step 2: Authorization and get code
*
* @param string $continueUrl
* @param string $baseUrl
* @param CookieJar $cookieJar
*
* @return string
* @throws GuzzleException
* @throws Exception
*/
private static function getCode(string $continueUrl, string $baseUrl, CookieJar $cookieJar): string {
$client = new Client(
[
'verify' => false,
'http_errors' => false,
'allow_redirects' => false, // Disable automatic redirects
'cookies' => $cookieJar
]
);
$params = [
'client_id' => 'web',
'prompt' => 'none',
'redirect_uri' => $baseUrl . self::REDIRECT_URL,
'response_mode' => 'query',
'response_type' => 'code',
'scope' => 'openid profile offline_access email'
];
$response = $client->get(
$continueUrl,
[
'query' => $params
]
);
if ($response->getStatusCode() !== 302) {
// Add debugging to understand what is happening
$body = $response->getBody()->getContents();
throw new Exception(
\sprintf(
'Authorization failed: Expected status code 302 but received %d. Message: %s. Body: %s',
$response->getStatusCode(),
$response->getReasonPhrase(),
$body
)
);
}
$location = $response->getHeader('Location')[0] ?? '';
if (empty($location)) {
throw new Exception('Missing Location header in authorization response');
}
parse_str(parse_url($location, PHP_URL_QUERY), $queryParams);
// Check for errors
if (isset($queryParams['error'])) {
throw new Exception(
\sprintf(
'Authorization error: %s - %s',
$queryParams['error'],
urldecode($queryParams['error_description'] ?? 'No description')
)
);
}
if (!isset($queryParams['code'])) {
throw new Exception('Missing auth code in redirect URL. Location: ' . $location);
}
return $queryParams['code'];
}
/**
* Step 3: Get token
*
* @param string $code
* @param string $baseUrl
* @param CookieJar $cookieJar
*
* @return array
*
* @throws GuzzleException
* @throws Exception
*
*/
private static function getToken(string $code, string $baseUrl, CookieJar $cookieJar): array {
$client = new Client(
[
'verify' => false,
'http_errors' => false,
'allow_redirects' => false,
'cookies' => $cookieJar
]
);
$response = $client->post(
$baseUrl . self::TOKEN_URL,
[
'form_params' => [
'client_id' => 'web',
'code' => $code,
'redirect_uri' => $baseUrl . self::REDIRECT_URL,
'grant_type' => 'authorization_code'
]
]
);
if ($response->getStatusCode() !== 200) {
throw new Exception(
\sprintf(
'Token request failed: Expected status code 200 but received %d. Message: %s',
$response->getStatusCode(),
$response->getReasonPhrase()
)
);
}
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['access_token']) || !isset($data['refresh_token'])) {
throw new Exception('Missing tokens in response');
}
return [
'access_token' => $data['access_token'],
'refresh_token' => $data['refresh_token']
];
}
}

View File

@@ -25,6 +25,7 @@ use Behat\Behat\Context\Context;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\HttpRequestHelper;
use TestHelpers\BehatHelper;
use TestHelpers\TokenHelper;
use TestHelpers\WebDavHelper;
/**
@@ -714,4 +715,27 @@ class AuthContext implements Context {
);
$this->featureContext->setResponse($response);
}
/**
* @When user :user should not be able to log in with wrong password :password
*
* @param string $user
* @param string $password
*
* @return void
*/
public function userShouldNotBeAbleToLogInWithWrongPassword(
string $user,
string $password
): void {
TokenHelper::clearUserTokens($user, $this->featureContext->getBaseUrl());
$response = TokenHelper::makeLoginRequest(
$user,
$password,
$this->featureContext->getBaseUrl(),
new \GuzzleHttp\Cookie\CookieJar()
);
// why is not 401 returned?
$this->featureContext->theHTTPStatusCodeShouldBe(204, 'should not be able to log in', $response);
}
}

View File

@@ -696,6 +696,10 @@ class CliContext implements Context {
* @return void
*/
public function theAdministratorReadsTheFileContent(string $user, string $file): void {
// this downloads the file using WebDAV and by that checks if it's still in
// postprocessing. So its effectively a check for finished postprocessing
$this->featureContext->userDownloadsFileUsingTheAPI($user, $file);
$userUuid = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$storagePath = $this->getUsersStoragePath();
$body = [
@@ -739,6 +743,10 @@ class CliContext implements Context {
* @return void
*/
public function theAdministratorRenamesFile(string $user, string $file, string $newName): void {
// this downloads the file using WebDAV and by that checks if it's still in
// postprocessing. So its effectively a check for finished postprocessing
$this->featureContext->userDownloadsFileUsingTheAPI($user, $file);
$userUuid = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$storagePath = $this->getUsersStoragePath();
@@ -763,6 +771,10 @@ class CliContext implements Context {
* @return void
*/
public function theAdministratorMovesFileToFolder(string $user, string $file, string $folder): void {
// this downloads the file using WebDAV and by that checks if it's still in
// postprocessing. So its effectively a check for finished postprocessing
$this->featureContext->userDownloadsFileUsingTheAPI($user, $file);
$userUuid = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$storagePath = $this->getUsersStoragePath();
@@ -874,6 +886,10 @@ class CliContext implements Context {
* @return void
*/
public function theAdminChecksTheAttributeOfFileForUser(string $attribute, string $file, string $user): void {
// this downloads the file using WebDAV and by that checks if it's still in
// postprocessing. So its effectively a check for finished postprocessing
$this->featureContext->userDownloadsFileUsingTheAPI($user, $file);
$userUuid = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$storagePath = $this->getUsersStoragePath();
$body = [

View File

@@ -17,6 +17,7 @@ use TestHelpers\GraphHelper;
use TestHelpers\WebDavHelper;
use TestHelpers\HttpRequestHelper;
use TestHelpers\BehatHelper;
use TestHelpers\TokenHelper;
require_once 'bootstrap.php';
@@ -2864,6 +2865,7 @@ class GraphContext implements Context {
);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
$this->featureContext->updateUsernameInCreatedUserList($byUser, $userName);
TokenHelper::clearUserTokens($byUser, $this->featureContext->getBaseUrl());
}
/**

View File

@@ -31,6 +31,7 @@ use TestHelpers\WebDavHelper;
use TestHelpers\GraphHelper;
use Laminas\Ldap\Exception\LdapException;
use Laminas\Ldap\Ldap;
use TestHelpers\TokenHelper;
/**
* Functions for provisioning of users and groups
@@ -558,110 +559,65 @@ trait Provisioning {
*/
public function usersHaveBeenCreated(
TableNode $table,
bool $useDefault=true,
bool $initialize=true
bool $useDefault = true,
bool $initialize = true
) {
$this->verifyTableNodeColumns($table, ['username'], ['displayname', 'email', 'password']);
$table = $table->getColumnsHash();
$users = $this->buildUsersAttributesArray($useDefault, $table);
$requests = [];
$client = HttpRequestHelper::createClient(
$this->getAdminUsername(),
$this->getAdminPassword()
);
foreach ($users as $userAttributes) {
$userName = $userAttributes['userid'];
$password = $userAttributes['password'];
$displayName = $userAttributes['displayName'];
$email = $userAttributes['email'];
if ($this->isTestingWithLdap()) {
$this->createLdapUser($userAttributes);
} else {
$attributesToCreateUser['userid'] = $userAttributes['userid'];
$attributesToCreateUser['password'] = $userAttributes['password'];
$attributesToCreateUser['displayname'] = $userAttributes['displayName'];
if ($userAttributes['email'] === null) {
Assert::assertArrayHasKey(
'userid',
$userAttributes,
__METHOD__ . " userAttributes array does not have key 'userid'"
try {
$this->createLdapUser($userAttributes);
} catch (LdapException $exception) {
throw new Exception(
__METHOD__ . " cannot create a LDAP user with provided data. Error: $exception"
);
$attributesToCreateUser['email'] = $userAttributes['userid'] . '@opencloud.eu';
} else {
$attributesToCreateUser['email'] = $userAttributes['email'];
}
$body = GraphHelper::prepareCreateUserPayload(
$attributesToCreateUser['userid'],
$attributesToCreateUser['password'],
$attributesToCreateUser['email'],
$attributesToCreateUser['displayname']
);
$request = GraphHelper::createRequest(
} else {
// Use the same logic as userHasBeenCreated for email generation
if ($email === null) {
$email = $this->getEmailAddressForUser($userName);
if ($email === null) {
// escape @ & space if present in userId
$email = \str_replace(["@", " "], "", $userName) . '@opencloud.eu';
}
}
$userName = $this->getActualUsername($userName);
$userName = \trim($userName);
$response = GraphHelper::createUser(
$this->getBaseUrl(),
$this->getStepLineRef(),
"POST",
'users',
$body,
$this->getAdminUsername(),
$this->getAdminPassword(),
$userName,
$password,
$email,
$displayName,
);
// Add the request to the $requests array so that they can be sent in parallel.
$requests[] = $request;
Assert::assertEquals(
201,
$response->getStatusCode(),
__METHOD__ . " cannot create user '$userName' using Graph API.\nResponse:" .
json_encode($this->getJsonDecodedResponse($response))
);
$userId = $this->getJsonDecodedResponse($response)['id'];
}
}
$exceptionToThrow = null;
if (!$this->isTestingWithLdap()) {
$results = HttpRequestHelper::sendBatchRequest($requests, $client);
// Check all requests to inspect failures.
foreach ($results as $key => $e) {
if ($e instanceof ClientException) {
$responseBody = $this->getJsonDecodedResponse($e->getResponse());
$httpStatusCode = $e->getResponse()->getStatusCode();
$graphStatusCode = $responseBody['error']['code'];
$messageText = $responseBody['error']['message'];
$exceptionToThrow = new Exception(
__METHOD__ .
" Unexpected failure when creating the user '" .
$users[$key]['userid'] . "'" .
"\nHTTP status $httpStatusCode " .
"\nGraph status $graphStatusCode " .
"\nError message $messageText"
);
}
}
}
$this->addUserToCreatedUsersList($userName, $password, $displayName, $email, $userId ?? null);
// Create requests for setting displayname and email for the newly created users.
// These values cannot be set while creating the user, so we have to edit the newly created user to set these values.
foreach ($users as $userAttributes) {
if (!$this->isTestingWithLdap()) {
// for graph api, we need to save the user id to be able to add it in some group
// can be fetched with the "onPremisesSamAccountName" i.e. userid
$response = $this->graphContext->adminHasRetrievedUserUsingTheGraphApi($userAttributes['userid']);
$userAttributes['id'] = $this->getJsonDecodedResponse($response)['id'];
} else {
$userAttributes['id'] = null;
}
$this->addUserToCreatedUsersList(
$userAttributes['userid'],
$userAttributes['password'],
$userAttributes['displayName'],
$userAttributes['email'],
$userAttributes['id']
);
}
if (isset($exceptionToThrow)) {
throw $exceptionToThrow;
}
foreach ($users as $user) {
Assert::assertTrue(
$this->userExists($user["userid"]),
"User '" . $user["userid"] . "' should exist but does not exist"
);
}
if ($initialize) {
foreach ($users as $user) {
$this->initializeUser($user['userid'], $user['password']);
if ($initialize) {
$this->initializeUser($userName, $password);
}
}
}
@@ -841,45 +797,16 @@ trait Provisioning {
*/
public function userHasBeenDeleted(string $user): void {
$user = $this->getActualUsername($user);
if ($this->userExists($user)) {
if ($this->isTestingWithLdap() && \in_array($user, $this->ldapCreatedUsers)) {
$this->deleteLdapUser($user);
} else {
$response = $this->deleteUser($user);
$this->theHTTPStatusCodeShouldBe(204, "", $response);
WebDavHelper::removeSpaceIdReferenceForUser($user);
}
if ($this->isTestingWithLdap() && \in_array($user, $this->ldapCreatedUsers)) {
$this->deleteLdapUser($user);
} else {
$response = $this->deleteUser($user);
$this->theHTTPStatusCodeShouldBe(204, "", $response);
WebDavHelper::removeSpaceIdReferenceForUser($user);
}
Assert::assertFalse(
$this->userExists($user),
"User '$user' should not exist but does exist"
);
$this->rememberThatUserIsNotExpectedToExist($user);
}
/**
* @Given these users have been initialized:
* expects a table of users with the heading
* "|username|password|"
*
* @param TableNode $table
*
* @return void
*/
public function theseUsersHaveBeenInitialized(TableNode $table): void {
foreach ($table as $row) {
if (!isset($row ['password'])) {
$password = $this->getPasswordForUser($row ['username']);
} else {
$password = $row ['password'];
}
$this->initializeUser(
$row ['username'],
$password
);
}
}
/**
* get all the existing groups
*
@@ -961,13 +888,14 @@ trait Provisioning {
$url = $this->getBaseUrl()
. "/ocs/v$this->ocsApiVersion.php/cloud/users/$user";
}
HttpRequestHelper::get(
$url,
$this->getStepLineRef(),
$user,
$password
);
if ($password !== '') {
HttpRequestHelper::get(
$url,
$this->getStepLineRef(),
$user,
$password
);
}
}
/**
@@ -1162,12 +1090,6 @@ trait Provisioning {
}
$this->addUserToCreatedUsersList($user, $password, $displayName, $email, $userId);
Assert::assertTrue(
$this->userExists($user),
"User '$user' should exist but does not exist"
);
$this->initializeUser($user, $password);
}
@@ -1999,21 +1921,15 @@ trait Provisioning {
$this->usingServer('LOCAL');
foreach ($this->createdUsers as $userData) {
$user = $userData['actualUsername'];
TokenHelper::clearUserTokens($user, $this->getBaseUrl());
$this->deleteUser($user);
Assert::assertFalse(
$this->userExists($user),
"User '$user' should not exist but does exist"
);
$this->rememberThatUserIsNotExpectedToExist($user);
}
$this->usingServer('REMOTE');
foreach ($this->createdRemoteUsers as $userData) {
$user = $userData['actualUsername'];
TokenHelper::clearUserTokens($user, $this->getBaseUrl());
$this->deleteUser($user);
Assert::assertFalse(
$this->userExists($user),
"User '$user' should not exist but does exist"
);
$this->rememberThatUserIsNotExpectedToExist($user);
}
$this->usingServer($previousServer);

View File

@@ -741,6 +741,17 @@ trait WebDav {
$this->setResponse($this->downloadFileWithRange($user, $fileSource, $range));
}
/**
* @When the user waits for :time seconds for postprocessing to finish
*
* @param int $time
*
* @return void
*/
public function waitForCertainSeconds(int $time): void {
\sleep($time);
}
/**
* @Then /^user "([^"]*)" using password "([^"]*)" should not be able to download file "([^"]*)"$/
*

View File

@@ -120,3 +120,26 @@ Feature: Propfind test
| Manager | RDNVWZP |
| Space Editor | DNVW |
| Space Viewer | |
@issue-1523
Scenario: propfind response contains a restored folder with correct name
Given user "Alice" has created a folder "folderMain" in space "Personal"
And user "Alice" has deleted folder "folderMain"
And user "Alice" has created a folder "folderMain" in space "Personal"
When user "Alice" restores the folder with original path "/folderMain" to "/folderMain (1)" using the trashbin API
And user "Alice" sends PROPFIND request to space "Personal" using the WebDAV API
Then the HTTP status code should be "207"
And as user "Alice" the PROPFIND response should contain a resource "folderMain" with these key and value pairs:
| key | value |
| oc:fileid | %file_id_pattern% |
| oc:file-parent | %file_id_pattern% |
| oc:name | folderMain |
| oc:permissions | RDNVCKZP |
| oc:size | 0 |
And as user "Alice" the PROPFIND response should contain a resource "folderMain (1)" with these key and value pairs:
| key | value |
| oc:fileid | %file_id_pattern% |
| oc:file-parent | %file_id_pattern% |
| oc:name | folderMain (1) |
| oc:permissions | RDNVCKZP |
| oc:size | 0 |

View File

@@ -219,7 +219,7 @@ Feature: edit user
When the user "Brian" resets the password of user "Carol" to "newpassword" using the Graph API
Then the HTTP status code should be "403"
And the content of file "resetpassword.txt" for user "Carol" using password "1234" should be "test file for reset password"
But user "Carol" using password "newpassword" should not be able to download file "resetpassword.txt"
And user "Carol" should not be able to log in with wrong password "newpassword"
Examples:
| user-role | user-role-2 |
| Space Admin | Space Admin |

View File

@@ -542,6 +542,7 @@ Feature: enable or disable sync of incoming shares
| sharee | Brian |
| shareType | user |
| permissionsRole | Viewer |
And user "Brian" has a share "textfile0.txt" synced
And the user "Admin" has deleted a user "Alice"
When user "Brian" disables sync of share "textfile0.txt" using the Graph API
Then the HTTP status code should be "204"
@@ -820,6 +821,7 @@ Feature: enable or disable sync of incoming shares
| sharee | Brian |
| shareType | user |
| permissionsRole | Viewer |
And user "Brian" has a share "<resource>" synced
And user "Brian" has disabled sync of last shared resource
When user "Brian" disables sync of share "<resource>" using the Graph API
Then the HTTP status code should be "409"

View File

@@ -14,7 +14,7 @@ Feature: reset user password via CLI command
But the command output should not contain "Failed to update user password: entry does not exist"
And the administrator has started the server
And user "Alice" should be able to create folder "newFolder" using password "newpass"
But user "Alice" should not be able to create folder "anotherFolder" using password "%alt1%"
But user "Alice" should not be able to log in with wrong password "%alt1%"
Scenario: try to reset password of non-existing user

View File

@@ -567,3 +567,52 @@ Feature: restore deleted files/folders
| dav-path-version |
| spaces |
| new |
@issue-1523
Scenario Outline: restore deleted folder when folder with same name exists
Given using <dav-path-version> DAV path
And user "Alice" has created folder "new"
And user "Alice" has uploaded file with content "content" to "new/test.txt"
And user "Alice" has deleted folder "new"
And user "Alice" has created folder "new"
And user "Alice" has uploaded file with content "new content" to "new/new-file.txt"
When user "Alice" restores the folder with original path "/new" to "/new (1)" using the trashbin API
Then the HTTP status code should be "201"
And as "Alice" the following folders should exist
| path |
| /new |
| /new (1) |
And as "Alice" the following files should exist
| path |
| /new/new-file.txt |
| /new (1)/test.txt |
Examples:
| dav-path-version |
| spaces |
| new |
@issue-1523
Scenario Outline: restore deleted folder with files when folder with same name exists
Given using <dav-path-version> DAV path
And user "Alice" has created folder "folder-a"
And user "Alice" has uploaded file with content "content b" to "folder-a/b.txt"
And user "Alice" has uploaded file with content "content c" to "folder-a/c.txt"
And user "Alice" has deleted file "folder-a/b.txt"
And user "Alice" has deleted folder "folder-a"
And user "Alice" has created folder "folder-a"
When user "Alice" restores the file with original path "folder-a/b.txt" using the trashbin API
Then the HTTP status code should be "201"
When user "Alice" restores the folder with original path "/folder-a" to "/folder-a (1)" using the trashbin API
Then the HTTP status code should be "201"
And as "Alice" the following folders should exist
| path |
| /folder-a |
| /folder-a (1) |
And as "Alice" the following files should exist
| path |
| /folder-a/b.txt |
| /folder-a (1)/c.txt |
Examples:
| dav-path-version |
| spaces |
| new |

View File

@@ -50,6 +50,7 @@ Feature: low level tests for upload of chunks
| Upload-Metadata | filename ZmlsZS50eHQ= |
When user "Alice" sends a chunk to the last created TUS Location with offset "0" and data "123" using the WebDAV API
And user "Alice" sends a chunk to the last created TUS Location with offset "3" and data "4567890" using the WebDAV API
And the user waits for "2" seconds for postprocessing to finish
And user "Alice" sends a chunk to the last created TUS Location with offset "3" and data "0000000" using the WebDAV API
Then the HTTP status code should be "404"
And the content of file "/file.txt" for user "Alice" should be "1234567890"

View File

@@ -4,7 +4,6 @@
[![Coverage Status](https://coveralls.io/repos/github/blevesearch/bleve/badge.svg?branch=master)](https://coveralls.io/github/blevesearch/bleve?branch=master)
[![Go Reference](https://pkg.go.dev/badge/github.com/blevesearch/bleve/v2.svg)](https://pkg.go.dev/github.com/blevesearch/bleve/v2)
[![Join the chat](https://badges.gitter.im/join_chat.svg)](https://app.gitter.im/#/room/#blevesearch_bleve:gitter.im)
[![codebeat](https://codebeat.co/badges/38a7cbc9-9cf5-41c0-a315-0746178230f4)](https://codebeat.co/projects/github-com-blevesearch-bleve)
[![Go Report Card](https://goreportcard.com/badge/github.com/blevesearch/bleve/v2)](https://goreportcard.com/report/github.com/blevesearch/bleve/v2)
[![Sourcegraph](https://sourcegraph.com/github.com/blevesearch/bleve/-/badge.svg)](https://sourcegraph.com/github.com/blevesearch/bleve?badge)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@@ -27,6 +26,8 @@ A modern indexing + search library in GO
* [synonym search](https://github.com/blevesearch/bleve/blob/master/docs/synonyms.md)
* [tf-idf](https://github.com/blevesearch/bleve/blob/master/docs/scoring.md#tf-idf) / [bm25](https://github.com/blevesearch/bleve/blob/master/docs/scoring.md#bm25) scoring models
* Hybrid search: exact + semantic
* Supports [RRF (Reciprocal Rank Fusion) and RSF (Relative Score Fusion)](docs/score_fusion.md)
* [Result pagination](https://github.com/blevesearch/bleve/blob/master/docs/pagination.md)
* Query time boosting
* Search result match highlighting with document fragments
* Aggregations/faceting support:

View File

@@ -68,7 +68,7 @@ func newBuilder(path string, mapping mapping.IndexMapping, config map[string]int
return nil, err
}
config["internal"] = map[string][]byte{
string(mappingInternalKey): mappingBytes,
string(util.MappingInternalKey): mappingBytes,
}
// do not use real config, as these are options for the builder,

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2025 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fusion
import (
"github.com/blevesearch/bleve/v2/search"
)
type FusionResult struct {
Hits search.DocumentMatchCollection
Total uint64
MaxScore float64
}

131
vendor/github.com/blevesearch/bleve/v2/fusion/rrf.go generated vendored Normal file
View File

@@ -0,0 +1,131 @@
// Copyright (c) 2025 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fusion
import (
"fmt"
"sort"
"github.com/blevesearch/bleve/v2/search"
)
func formatRRFMessage(weight float64, rank int, rankConstant int) string {
return fmt.Sprintf("rrf score (weight=%.3f, rank=%d, rank_constant=%d), normalized score of", weight, rank, rankConstant)
}
// ReciprocalRankFusion performs a reciprocal rank fusion on the search results.
func ReciprocalRankFusion(hits search.DocumentMatchCollection, weights []float64, rankConstant int, windowSize int, numKNNQueries int, explain bool) FusionResult {
if len(hits) == 0 {
return FusionResult{
Hits: hits,
Total: 0,
MaxScore: 0.0,
}
}
// Create a map of document ID to a slice of ranks.
// The first element of the slice is the rank from the FTS search,
// and the subsequent elements are the ranks from the KNN searches.
docRanks := make(map[string][]int)
// Pre-assign rank lists to each candidate document
for _, hit := range hits {
docRanks[hit.ID] = make([]int, numKNNQueries+1)
}
// Only a max of `window_size` elements need to be counted for. Stop
// calculating rank once this threshold is hit.
sort.Slice(hits, func(a, b int) bool {
return scoreSortFunc()(hits[a], hits[b]) < 0
})
// Only consider top windowSize docs for rescoring
for i := range min(windowSize, len(hits)) {
if hits[i].Score != 0.0 {
// Skip if Score is 0, since that means the document was not
// found as part of FTS, and only in KNN.
docRanks[hits[i].ID][0] = i + 1
}
}
// Allocate knnDocs and reuse it within the loop
knnDocs := make([]*search.DocumentMatch, 0, len(hits))
// For each KNN query, rank the documents based on their KNN score.
for i := range numKNNQueries {
knnDocs = knnDocs[:0]
for _, hit := range hits {
if _, ok := hit.ScoreBreakdown[i]; ok {
knnDocs = append(knnDocs, hit)
}
}
// Sort the documents based on their score for this KNN query.
sort.Slice(knnDocs, func(a, b int) bool {
return scoreBreakdownSortFunc(i)(knnDocs[a], knnDocs[b]) < 0
})
// Update the ranks of the documents in the docRanks map.
// Only consider top windowSize docs for rescoring.
for j := range min(windowSize, len(knnDocs)) {
docRanks[knnDocs[j].ID][i+1] = j + 1
}
}
// Calculate the RRF score for each document.
var maxScore float64
for _, hit := range hits {
var rrfScore float64
var explChildren []*search.Explanation
if explain {
explChildren = make([]*search.Explanation, 0, numKNNQueries+1)
}
for i, rank := range docRanks[hit.ID] {
if rank > 0 {
partialRrfScore := weights[i] * 1.0 / float64(rankConstant+rank)
if explain {
expl := getFusionExplAt(
hit,
i,
partialRrfScore,
formatRRFMessage(weights[i], rank, rankConstant),
)
explChildren = append(explChildren, expl)
}
rrfScore += partialRrfScore
}
}
hit.Score = rrfScore
hit.ScoreBreakdown = nil
if rrfScore > maxScore {
maxScore = rrfScore
}
if explain {
finalizeFusionExpl(hit, explChildren)
}
}
sort.Sort(hits)
if len(hits) > windowSize {
hits = hits[:windowSize]
}
return FusionResult{
Hits: hits,
Total: uint64(len(hits)),
MaxScore: maxScore,
}
}

162
vendor/github.com/blevesearch/bleve/v2/fusion/rsf.go generated vendored Normal file
View File

@@ -0,0 +1,162 @@
// Copyright (c) 2025 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fusion
import (
"fmt"
"sort"
"github.com/blevesearch/bleve/v2/search"
)
func formatRSFMessage(weight float64, normalizedScore float64, minScore float64, maxScore float64) string {
return fmt.Sprintf("rsf score (weight=%.3f, normalized=%.6f, min=%.6f, max=%.6f), normalized score of",
weight, normalizedScore, minScore, maxScore)
}
// RelativeScoreFusion normalizes scores based on min/max values for FTS and each KNN query, then applies weights.
func RelativeScoreFusion(hits search.DocumentMatchCollection, weights []float64, windowSize int, numKNNQueries int, explain bool) FusionResult {
if len(hits) == 0 {
return FusionResult{
Hits: hits,
Total: 0,
MaxScore: 0.0,
}
}
rsfScores := make(map[string]float64)
// contains the docs under consideration for scoring.
// Reused for fts and knn hits
scoringDocs := make([]*search.DocumentMatch, 0, len(hits))
var explMap map[string][]*search.Explanation
if explain {
explMap = make(map[string][]*search.Explanation)
}
// remove non-fts hits
for _, hit := range hits {
if hit.Score != 0.0 {
scoringDocs = append(scoringDocs, hit)
}
}
// sort hits by fts score
sort.Slice(scoringDocs, func(a, b int) bool {
return scoreSortFunc()(scoringDocs[a], scoringDocs[b]) < 0
})
// Reslice to correct size
if len(scoringDocs) > windowSize {
scoringDocs = scoringDocs[:windowSize]
}
var min, max float64
if len(scoringDocs) > 0 {
min, max = scoringDocs[len(scoringDocs)-1].Score, scoringDocs[0].Score
}
for _, hit := range scoringDocs {
var tempRsfScore float64
if max > min {
tempRsfScore = (hit.Score - min) / (max - min)
} else {
tempRsfScore = 1.0
}
if explain {
// create and replace new explanation
expl := getFusionExplAt(
hit,
0,
tempRsfScore,
formatRSFMessage(weights[0], tempRsfScore, min, max),
)
explMap[hit.ID] = append(explMap[hit.ID], expl)
}
rsfScores[hit.ID] = weights[0] * tempRsfScore
}
for i := range numKNNQueries {
scoringDocs = scoringDocs[:0]
for _, hit := range hits {
if _, exists := hit.ScoreBreakdown[i]; exists {
scoringDocs = append(scoringDocs, hit)
}
}
sort.Slice(scoringDocs, func(a, b int) bool {
return scoreBreakdownSortFunc(i)(scoringDocs[a], scoringDocs[b]) < 0
})
if len(scoringDocs) > windowSize {
scoringDocs = scoringDocs[:windowSize]
}
if len(scoringDocs) > 0 {
min, max = scoringDocs[len(scoringDocs)-1].ScoreBreakdown[i], scoringDocs[0].ScoreBreakdown[i]
} else {
min, max = 0.0, 0.0
}
for _, hit := range scoringDocs {
var tempRsfScore float64
if max > min {
tempRsfScore = (hit.ScoreBreakdown[i] - min) / (max - min)
} else {
tempRsfScore = 1.0
}
if explain {
expl := getFusionExplAt(
hit,
i+1,
tempRsfScore,
formatRSFMessage(weights[i+1], tempRsfScore, min, max),
)
explMap[hit.ID] = append(explMap[hit.ID], expl)
}
rsfScores[hit.ID] += weights[i+1] * tempRsfScore
}
}
var maxScore float64
for _, hit := range hits {
if rsfScore, exists := rsfScores[hit.ID]; exists {
hit.Score = rsfScore
if rsfScore > maxScore {
maxScore = rsfScore
}
if explain {
finalizeFusionExpl(hit, explMap[hit.ID])
}
} else {
hit.Score = 0.0
}
hit.ScoreBreakdown = nil
}
sort.Sort(hits)
if len(hits) > windowSize {
hits = hits[:windowSize]
}
return FusionResult{
Hits: hits,
Total: uint64(len(hits)),
MaxScore: maxScore,
}
}

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