mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-26 15:50:47 -05:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24210c7b3a | ||
|
|
8565ed9277 | ||
|
|
75b0cd9909 | ||
|
|
b940b0c457 | ||
|
|
6e5e5a7e8a | ||
|
|
62a7f79f51 | ||
|
|
b1968591b1 | ||
|
|
05b80b7f63 | ||
|
|
9659e97056 | ||
|
|
8238a4091a | ||
|
|
0f09cdd8ec | ||
|
|
b9f48edd87 | ||
|
|
926a2c2080 | ||
|
|
7b3e8444d1 | ||
|
|
13c3f42396 | ||
|
|
7c59e57d43 | ||
|
|
7a7d148dcf | ||
|
|
3c8e2dacfd | ||
|
|
77ddcc2d6b | ||
|
|
e255f81279 | ||
|
|
1afc1331af | ||
|
|
6d86b35651 | ||
|
|
977d706a43 | ||
|
|
4bafe2e611 | ||
|
|
99cf64bf32 | ||
|
|
13c3368660 | ||
|
|
e15443d951 | ||
|
|
ad9f8b3b4a | ||
|
|
b4cc781832 | ||
|
|
2670f24605 | ||
|
|
b865d7c2f2 | ||
|
|
d3f73b13ff | ||
|
|
55f1d9e4d2 | ||
|
|
484f8bc66b | ||
|
|
4c2b723ab0 | ||
|
|
683b979bdd | ||
|
|
a6cdbc710d | ||
|
|
49ab88e980 |
@@ -1,3 +1,3 @@
|
||||
# The test runner source for UI tests
|
||||
WEB_COMMITID=e060701fc4ef5449a482e501b69e2b5cbf873466
|
||||
WEB_COMMITID=636c9d41f100901c4f75509b3269dffaa94d8004
|
||||
WEB_BRANCH=main
|
||||
|
||||
195
.woodpecker.star
195
.woodpecker.star
@@ -33,6 +33,7 @@ PLUGINS_S3_CACHE = "plugins/s3-cache:1"
|
||||
PLUGINS_SLACK = "plugins/slack:1"
|
||||
REDIS = "redis:6-alpine"
|
||||
READY_RELEASE_GO = "woodpeckerci/plugin-ready-release-go:latest"
|
||||
OPENLDAP = "bitnami/openldap:2.6"
|
||||
|
||||
DEFAULT_PHP_VERSION = "8.2"
|
||||
DEFAULT_NODEJS_VERSION = "20"
|
||||
@@ -45,6 +46,7 @@ dirs = {
|
||||
"zip": "/woodpecker/src/github.com/opencloud-eu/opencloud/zip",
|
||||
"webZip": "/woodpecker/src/github.com/opencloud-eu/opencloud/zip/web.tar.gz",
|
||||
"webPnpmZip": "/woodpecker/src/github.com/opencloud-eu/opencloud/zip/web-pnpm.tar.gz",
|
||||
"playwrightBrowsersArchive": "/woodpecker/src/github.com/opencloud-eu/opencloud/zip/playwright-browsers.tar.gz",
|
||||
"baseGo": "/go/src/github.com/opencloud-eu/opencloud",
|
||||
"gobinTar": "go-bin.tar.gz",
|
||||
"gobinTarPath": "/go/src/github.com/opencloud-eu/opencloud/go-bin.tar.gz",
|
||||
@@ -112,7 +114,7 @@ config = {
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"emailNeeded": True,
|
||||
"extraEnvironment": {
|
||||
"extraTestEnvironment": {
|
||||
"EMAIL_HOST": "email",
|
||||
"EMAIL_PORT": "9000",
|
||||
},
|
||||
@@ -206,7 +208,7 @@ config = {
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"emailNeeded": True,
|
||||
"extraEnvironment": {
|
||||
"extraTestEnvironment": {
|
||||
"EMAIL_HOST": "email",
|
||||
"EMAIL_PORT": "9000",
|
||||
},
|
||||
@@ -249,7 +251,7 @@ config = {
|
||||
"withRemotePhp": [True],
|
||||
"federationServer": True,
|
||||
"emailNeeded": True,
|
||||
"extraEnvironment": {
|
||||
"extraTestEnvironment": {
|
||||
"EMAIL_HOST": "email",
|
||||
"EMAIL_PORT": "9000",
|
||||
},
|
||||
@@ -299,6 +301,36 @@ config = {
|
||||
"STORAGE_USERS_DRIVER": "decomposed",
|
||||
},
|
||||
},
|
||||
"multiTenancy": {
|
||||
"suites": [
|
||||
"apiTenancy",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"ldapNeeded": True,
|
||||
"extraTestEnvironment": {
|
||||
"USE_PREPARED_LDAP_USERS": True,
|
||||
},
|
||||
"extraServerEnvironment": {
|
||||
"OC_LDAP_USER_SCHEMA_TENANT_ID": "departmentNumber",
|
||||
"OC_LDAP_URI": "ldaps://ldap-server:1636",
|
||||
"OC_LDAP_INSECURE": True,
|
||||
"OC_LDAP_BIND_DN": "cn=admin,dc=opencloud,dc=eu",
|
||||
"OC_LDAP_BIND_PASSWORD": "admin",
|
||||
"OC_LDAP_GROUP_BASE_DN": "ou=groups,dc=opencloud,dc=eu",
|
||||
"OC_LDAP_GROUP_SCHEMA_ID": "entryUUID",
|
||||
"OC_LDAP_USER_BASE_DN": "ou=users,dc=opencloud,dc=eu",
|
||||
"OC_LDAP_USER_FILTER": "(objectclass=inetOrgPerson)",
|
||||
"OC_LDAP_USER_SCHEMA_ID": "entryUUID",
|
||||
"OC_LDAP_DISABLE_USER_MECHANISM": "none",
|
||||
"GRAPH_LDAP_SERVER_UUID": True,
|
||||
"GRAPH_LDAP_GROUP_CREATE_BASE_DN": "ou=custom,ou=groups,dc=opencloud,dc=eu",
|
||||
"GRAPH_LDAP_REFINT_ENABLED": True,
|
||||
"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",
|
||||
},
|
||||
},
|
||||
},
|
||||
"apiTests": {
|
||||
"numberOfParts": 7,
|
||||
@@ -309,7 +341,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"], # suites to skip
|
||||
"xsuites": ["search", "app-provider", "app-provider-onlyOffice", "app-store", "keycloak", "oidc", "ocm", "a11y", "mobile-view"], # suites to skip
|
||||
},
|
||||
"search": {
|
||||
"skip": False,
|
||||
@@ -450,6 +482,7 @@ def main(ctx):
|
||||
checkGherkinLint(ctx) + \
|
||||
checkTestSuitesInExpectedFailures(ctx) + \
|
||||
buildWebCache(ctx) + \
|
||||
cacheBrowsers(ctx) + \
|
||||
getGoBinForTesting(ctx) + \
|
||||
buildOpencloudBinaryForTesting(ctx) + \
|
||||
checkStarlark(ctx) + \
|
||||
@@ -911,7 +944,7 @@ def localApiTestPipeline(ctx):
|
||||
defaults = {
|
||||
"suites": {},
|
||||
"skip": False,
|
||||
"extraEnvironment": {},
|
||||
"extraTestEnvironment": {},
|
||||
"extraServerEnvironment": {},
|
||||
"storages": storages,
|
||||
"accounts_hash_difficulty": 4,
|
||||
@@ -922,6 +955,7 @@ def localApiTestPipeline(ctx):
|
||||
"collaborationServiceNeeded": False,
|
||||
"extraCollaborationEnvironment": {},
|
||||
"withRemotePhp": with_remote_php,
|
||||
"ldapNeeded": False,
|
||||
}
|
||||
|
||||
if "localApiTests" in config:
|
||||
@@ -939,11 +973,13 @@ def localApiTestPipeline(ctx):
|
||||
(waitForServices("online-offices", ["collabora:9980", "onlyoffice:443", "fakeoffice:8080"]) if params["collaborationServiceNeeded"] else []) +
|
||||
(waitForClamavService() if params["antivirusNeeded"] else []) +
|
||||
(waitForEmailService() if params["emailNeeded"] else []) +
|
||||
(ldapService() if params["ldapNeeded"] else []) +
|
||||
(waitForLdapService() if params["ldapNeeded"] else []) +
|
||||
opencloudServer(storage, params["accounts_hash_difficulty"], extra_server_environment = params["extraServerEnvironment"], with_wrapper = True, tika_enabled = params["tikaNeeded"]) +
|
||||
(opencloudServer(storage, params["accounts_hash_difficulty"], deploy_type = "federation", extra_server_environment = params["extraServerEnvironment"]) if params["federationServer"] else []) +
|
||||
((wopiCollaborationService("fakeoffice") + wopiCollaborationService("collabora") + wopiCollaborationService("onlyoffice")) if params["collaborationServiceNeeded"] else []) +
|
||||
(openCloudHealthCheck("wopi", ["wopi-collabora:9304", "wopi-onlyoffice:9304", "wopi-fakeoffice:9304"]) if params["collaborationServiceNeeded"] else []) +
|
||||
localApiTests(name, params["suites"], storage, params["extraEnvironment"], run_with_remote_php) +
|
||||
localApiTests(name, params["suites"], storage, params["extraTestEnvironment"], run_with_remote_php) +
|
||||
logRequests(),
|
||||
"services": (emailService() if params["emailNeeded"] else []) +
|
||||
(clamavService() if params["antivirusNeeded"] else []) +
|
||||
@@ -1276,6 +1312,7 @@ def e2eTestPipeline(ctx):
|
||||
restoreBuildArtifactCache(ctx, dirs["opencloudBinArtifact"], dirs["opencloudBin"]) + \
|
||||
restoreWebCache() + \
|
||||
restoreWebPnpmCache() + \
|
||||
restoreBrowsersCache() + \
|
||||
(tikaService() if params["tikaNeeded"] else []) + \
|
||||
opencloudServer(storage, extra_server_environment = extra_server_environment, tika_enabled = params["tikaNeeded"])
|
||||
|
||||
@@ -1288,6 +1325,8 @@ def e2eTestPipeline(ctx):
|
||||
"RETRY": "1",
|
||||
"WEB_UI_CONFIG_FILE": "%s/%s" % (dirs["base"], dirs["opencloudConfig"]),
|
||||
"LOCAL_UPLOAD_DIR": "/uploads",
|
||||
"PLAYWRIGHT_BROWSERS_PATH": "%s/%s" % (dirs["base"], ".playwright"),
|
||||
"BROWSER": "chromium",
|
||||
"REPORT_TRACING": params["reportTracing"],
|
||||
},
|
||||
"commands": [
|
||||
@@ -2622,6 +2661,77 @@ def generateWebPnpmCache(ctx):
|
||||
},
|
||||
]
|
||||
|
||||
def cacheBrowsers(ctx):
|
||||
e2e_trigger = [
|
||||
event["base"],
|
||||
{
|
||||
"event": "pull_request",
|
||||
"path": {
|
||||
"exclude": skipIfUnchanged(ctx, "e2e-tests"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"event": "tag",
|
||||
"ref": "refs/tags/**",
|
||||
},
|
||||
]
|
||||
|
||||
check_browser_step = [{
|
||||
"name": "check-browsers-cache",
|
||||
"image": MINIO_MC,
|
||||
"environment": MINIO_MC_ENV,
|
||||
"commands": [
|
||||
"mc alias set s3 $MC_HOST $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY",
|
||||
"mc ls --recursive s3/$CACHE_BUCKET/web",
|
||||
"cd %s" % dirs["web"],
|
||||
"bash tests/woodpecker/script.sh check_browsers_cache",
|
||||
],
|
||||
}]
|
||||
|
||||
webPnpmCacheSteps = restoreWebPnpmCache(extra_commands = [
|
||||
". ./.woodpecker.env",
|
||||
"if $BROWSER_CACHE_FOUND; then exit 0; fi",
|
||||
])
|
||||
|
||||
browser_cache_steps = [
|
||||
{
|
||||
"name": "install-browsers",
|
||||
"image": OC_CI_NODEJS % DEFAULT_NODEJS_VERSION,
|
||||
"environment": {
|
||||
"PLAYWRIGHT_BROWSERS_PATH": ".playwright",
|
||||
},
|
||||
"commands": [
|
||||
". ./.woodpecker.env",
|
||||
"if $BROWSER_CACHE_FOUND; then exit 0; fi",
|
||||
"cd %s" % dirs["web"],
|
||||
"pnpm exec playwright install --with-deps",
|
||||
"pnpm exec playwright install --list",
|
||||
"tar -czf %s .playwright" % dirs["playwrightBrowsersArchive"],
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "upload-browsers-cache",
|
||||
"image": MINIO_MC,
|
||||
"environment": MINIO_MC_ENV,
|
||||
"commands": [
|
||||
". ./.woodpecker.env",
|
||||
"if $BROWSER_CACHE_FOUND; then exit 0; fi",
|
||||
"cd %s" % dirs["web"],
|
||||
"playwright_version=$(bash tests/woodpecker/script.sh get_playwright_version)",
|
||||
"mc alias set s3 $MC_HOST $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY",
|
||||
"mc cp -r -a %s s3/$CACHE_BUCKET/web/browsers-cache/$playwright_version/" % dirs["playwrightBrowsersArchive"],
|
||||
"mc ls --recursive s3/$CACHE_BUCKET/web",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return [{
|
||||
"name": "cache-browsers",
|
||||
"depends_on": getPipelineNames(buildWebCache(ctx)),
|
||||
"steps": restoreWebCache() + check_browser_step + webPnpmCacheSteps + browser_cache_steps,
|
||||
"when": e2e_trigger,
|
||||
}]
|
||||
|
||||
def generateWebCache(ctx):
|
||||
return [
|
||||
getWoodpeckerEnvAndCheckScript(ctx),
|
||||
@@ -2671,12 +2781,12 @@ def restoreWebCache():
|
||||
],
|
||||
}]
|
||||
|
||||
def restoreWebPnpmCache():
|
||||
def restoreWebPnpmCache(extra_commands = []):
|
||||
return [{
|
||||
"name": "restore-web-pnpm-cache",
|
||||
"image": MINIO_MC,
|
||||
"environment": MINIO_MC_ENV,
|
||||
"commands": [
|
||||
"commands": extra_commands + [
|
||||
"source ./.woodpecker.env",
|
||||
"mc alias set s3 $MC_HOST $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY",
|
||||
"mc cp -r -a s3/$CACHE_BUCKET/opencloud/web-test-runner/$WEB_COMMITID/web-pnpm.tar.gz %s" % dirs["zip"],
|
||||
@@ -2685,16 +2795,38 @@ def restoreWebPnpmCache():
|
||||
# we need to install again because the node_modules are not cached
|
||||
"name": "unzip-and-install-pnpm",
|
||||
"image": OC_CI_NODEJS % DEFAULT_NODEJS_VERSION,
|
||||
"commands": [
|
||||
"commands": extra_commands + [
|
||||
"cd %s" % dirs["web"],
|
||||
"rm -rf .pnpm-store",
|
||||
"tar -xvf %s" % dirs["webPnpmZip"],
|
||||
'npm install --silent --global --force "$(jq -r ".packageManager" < package.json)"',
|
||||
"pnpm config set store-dir ./.pnpm-store",
|
||||
"for i in $(seq 3); do pnpm install && break || sleep 1; done",
|
||||
"for i in $(seq 3); do pnpm install --no-frozen-lockfile && break || sleep 1; done",
|
||||
],
|
||||
}]
|
||||
|
||||
def restoreBrowsersCache():
|
||||
return [
|
||||
{
|
||||
"name": "restore-browsers-cache",
|
||||
"image": MINIO_MC,
|
||||
"environment": MINIO_MC_ENV,
|
||||
"commands": [
|
||||
"cd %s" % dirs["web"],
|
||||
"playwright_version=$(bash tests/woodpecker/script.sh get_playwright_version)",
|
||||
"mc alias set s3 $MC_HOST $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY",
|
||||
"mc cp -r -a s3/$CACHE_BUCKET/web/browsers-cache/$playwright_version/playwright-browsers.tar.gz %s" % dirs["web"],
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "unzip-browsers-cache",
|
||||
"image": OC_UBUNTU,
|
||||
"commands": [
|
||||
"tar -xvf /woodpecker/src/github.com/opencloud-eu/opencloud/webTestRunner/playwright-browsers.tar.gz -C .",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
def emailService():
|
||||
return [{
|
||||
"name": "email",
|
||||
@@ -2725,6 +2857,49 @@ def waitForClamavService():
|
||||
],
|
||||
}]
|
||||
|
||||
def ldapService():
|
||||
return [
|
||||
{
|
||||
"name": "ldap-server",
|
||||
"image": OPENLDAP,
|
||||
"detach": True,
|
||||
"environment": {
|
||||
"BITNAMI_DEBUG": "true",
|
||||
"LDAP_TLS_VERIFY_CLIENT": "never",
|
||||
"LDAP_ENABLE_TLS": "yes",
|
||||
"LDAP_TLS_CA_FILE": "/opt/bitnami/openldap/share/openldap.crt",
|
||||
"LDAP_TLS_CERT_FILE": "/opt/bitnami/openldap/share/openldap.crt",
|
||||
"LDAP_TLS_KEY_FILE": "/opt/bitnami/openldap/share/openldap.key",
|
||||
"LDAP_ROOT": "dc=opencloud,dc=eu",
|
||||
"LDAP_ADMIN_PASSWORD": "admin",
|
||||
},
|
||||
"commands": [
|
||||
"mkdir -p /opt/bitnami/openldap/share",
|
||||
"mkdir -p /tmp/custom-scripts",
|
||||
"mkdir -p /tmp/ldif-files",
|
||||
"cp tests/config/woodpecker/ldap/*.ldif /tmp/ldif-files/",
|
||||
"cp tests/config/woodpecker/ldap/docker-entrypoint-override.sh /tmp/custom-scripts/",
|
||||
"chmod +x /tmp/custom-scripts/docker-entrypoint-override.sh",
|
||||
"ls -la /tmp/ldif-files/",
|
||||
"/tmp/custom-scripts/docker-entrypoint-override.sh /opt/bitnami/scripts/openldap/run.sh",
|
||||
],
|
||||
"backend_options": {
|
||||
"docker": {
|
||||
"user": "0:0",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def waitForLdapService():
|
||||
return [{
|
||||
"name": "wait-for-ldap",
|
||||
"image": OC_CI_WAIT_FOR,
|
||||
"commands": [
|
||||
"wait-for -it ldap-server:1636 -t 600",
|
||||
],
|
||||
}]
|
||||
|
||||
def fakeOffice():
|
||||
return [
|
||||
{
|
||||
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,5 +1,50 @@
|
||||
# Changelog
|
||||
|
||||
## [3.3.0](https://github.com/opencloud-eu/opencloud/releases/tag/v3.3.0) - 2025-08-12
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@ScharfViktor, @aduffeck, @michaelstingl
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- Tenant [[#1274](https://github.com/opencloud-eu/opencloud/pull/1274)]
|
||||
|
||||
### 📈 Enhancement
|
||||
|
||||
- chore: bump web to v3.3.0 [[#1329](https://github.com/opencloud-eu/opencloud/pull/1329)]
|
||||
|
||||
### ✅ Tests
|
||||
|
||||
- multiTenancyTests [[#1313](https://github.com/opencloud-eu/opencloud/pull/1313)]
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Fix posix driver documentation in STORAGE_USERS_DRIVER description [[#1305](https://github.com/opencloud-eu/opencloud/pull/1305)]
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Improve indexing performance using batches [[#1306](https://github.com/opencloud-eu/opencloud/pull/1306)]
|
||||
- Do not run the timout func if the work func has run [[#1302](https://github.com/opencloud-eu/opencloud/pull/1302)]
|
||||
- Make sure to register prometheus collectors only once [[#1295](https://github.com/opencloud-eu/opencloud/pull/1295)]
|
||||
|
||||
### 📦️ Dependencies
|
||||
|
||||
- [decomposed] bump-version-v3.3.0 [[#1332](https://github.com/opencloud-eu/opencloud/pull/1332)]
|
||||
- [full-ci] Reva bump 2.36.0 [[#1328](https://github.com/opencloud-eu/opencloud/pull/1328)]
|
||||
- Bump reva [[#1315](https://github.com/opencloud-eu/opencloud/pull/1315)]
|
||||
|
||||
## [3.2.1](https://github.com/opencloud-eu/opencloud/releases/tag/v3.2.1) - 2025-07-30
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@aduffeck, @dragonchaser, @individual-it
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Do not try to log metrics when we failed to get the consumer info [[#1289](https://github.com/opencloud-eu/opencloud/pull/1289)]
|
||||
- Add thumbnails to sharedWithMe and sharedByMe requests [[#1257](https://github.com/opencloud-eu/opencloud/pull/1257)]
|
||||
|
||||
## [3.2.0](https://github.com/opencloud-eu/opencloud/releases/tag/v3.2.0) - 2025-07-21
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
@@ -32,6 +77,7 @@
|
||||
|
||||
### 📦️ Dependencies
|
||||
|
||||
- [decomposed] bump-version-v3.2.0 [[#1258](https://github.com/opencloud-eu/opencloud/pull/1258)]
|
||||
- [full-ci] Reva bump 2.35.0 [[#1255](https://github.com/opencloud-eu/opencloud/pull/1255)]
|
||||
- build(deps): bump golang.org/x/net from 0.41.0 to 0.42.0 [[#1232](https://github.com/opencloud-eu/opencloud/pull/1232)]
|
||||
- build(deps): bump github.com/KimMachineGun/automemlimit from 0.7.3 to 0.7.4 [[#1226](https://github.com/opencloud-eu/opencloud/pull/1226)]
|
||||
|
||||
6
go.mod
6
go.mod
@@ -14,7 +14,7 @@ require (
|
||||
github.com/blevesearch/bleve/v2 v2.5.2
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/coreos/go-oidc/v3 v3.14.1
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250703154118-810365dec814
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250725064958-2d9caef4db2a
|
||||
github.com/davidbyttow/govips/v2 v2.16.0
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
|
||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||
@@ -63,8 +63,8 @@ require (
|
||||
github.com/onsi/ginkgo/v2 v2.23.4
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/open-policy-agent/opa v1.6.0
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250707143759-32eaae12b2ce
|
||||
github.com/opencloud-eu/reva/v2 v2.35.0
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
|
||||
github.com/opencloud-eu/reva/v2 v2.36.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/xattr v0.4.12
|
||||
|
||||
12
go.sum
12
go.sum
@@ -244,8 +244,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo
|
||||
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||
github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
|
||||
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250703154118-810365dec814 h1:bo0vg45RDYHOJn33XhfRB830gqrlQJoCQjqUkR2fiAk=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250703154118-810365dec814/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250725064958-2d9caef4db2a h1:4IvTz3MUno/nlgngdyZhkyxzJR/w7+H+2ZXoZQKidgg=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250725064958-2d9caef4db2a/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
@@ -866,10 +866,10 @@ github.com/open-policy-agent/opa v1.6.0 h1:/S/cnNQJ2MUMNzizHPbisTWBHowmLkPrugY5j
|
||||
github.com/open-policy-agent/opa v1.6.0/go.mod h1:zFmw4P+W62+CWGYRDDswfVYSCnPo6oYaktQnfIaRFC4=
|
||||
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a h1:Sakl76blJAaM6NxylVkgSzktjo2dS504iDotEFJsh3M=
|
||||
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250707143759-32eaae12b2ce h1:tjbIYsW5CFsEbCf5B/KN0Mo1oKU/K+oipgFm2B6wzG4=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250707143759-32eaae12b2ce/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
|
||||
github.com/opencloud-eu/reva/v2 v2.35.0 h1:lKxGiI9yFD7MTeyFJa68BQD+DiB1rQvhC8QePa/Vlc4=
|
||||
github.com/opencloud-eu/reva/v2 v2.35.0/go.mod h1:UVPwuMjfgPekuh7unWavJSiPihgmk1GYF3xct0q3+X0=
|
||||
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.36.0 h1:5FBjhXqW8F4v7F76vGYpH7IGuRtcbKHoyOyj3syG7W8=
|
||||
github.com/opencloud-eu/reva/v2 v2.36.0/go.mod h1:/FyYaUWxtllu8TOcIIx53BjChc+hSpcQicBI/OTICjw=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
|
||||
@@ -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.2.0+dev"
|
||||
LatestTag = "3.3.0+dev"
|
||||
|
||||
// Date indicates the build date.
|
||||
// This has been removed, it looks like you can only replace static strings with recent go versions
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# 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:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-02 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: pkg/service/response.go:44
|
||||
msgid "description"
|
||||
msgstr "beschrijving"
|
||||
|
||||
#: pkg/service/response.go:43
|
||||
msgid "display name"
|
||||
msgstr "weergavenaam"
|
||||
|
||||
#: pkg/service/response.go:42
|
||||
msgid "expiration date"
|
||||
msgstr "verloopdatum"
|
||||
|
||||
#: pkg/service/response.go:41
|
||||
msgid "password"
|
||||
msgstr "wachtwoord"
|
||||
|
||||
#: pkg/service/response.go:40
|
||||
msgid "permission"
|
||||
msgstr "machtiging"
|
||||
|
||||
#: pkg/service/response.go:39
|
||||
msgid "some field"
|
||||
msgstr "een veld"
|
||||
|
||||
#: pkg/service/response.go:26
|
||||
msgid "{resource} was downloaded via public link {token}"
|
||||
msgstr "{resource} is gedownload via openbare link {token}"
|
||||
|
||||
#: pkg/service/response.go:24
|
||||
msgid "{user} added {resource} to {folder}"
|
||||
msgstr "{user} heeft {resource} toegevoegd aan {folder}"
|
||||
|
||||
#: pkg/service/response.go:36
|
||||
msgid "{user} added {sharee} as member of {space}"
|
||||
msgstr "{user} heeft {sharee} als lid toegevoegd van {space}"
|
||||
|
||||
#: pkg/service/response.go:27
|
||||
msgid "{user} deleted {resource} from {folder}"
|
||||
msgstr "{user} heeft {resource} verwijderd van {folder}"
|
||||
|
||||
#: pkg/service/response.go:28
|
||||
msgid "{user} moved {resource} to {folder}"
|
||||
msgstr "{user} heeft {resource} verplaatst naar {folder}"
|
||||
|
||||
#: pkg/service/response.go:35
|
||||
msgid "{user} removed link to {resource}"
|
||||
msgstr "{user} heeft link naar {resource} verwijderd"
|
||||
|
||||
#: pkg/service/response.go:32
|
||||
msgid "{user} removed {sharee} from {resource}"
|
||||
msgstr "{user} heeft {sharee} verwijderd van {resource}"
|
||||
|
||||
#: pkg/service/response.go:37
|
||||
msgid "{user} removed {sharee} from {space}"
|
||||
msgstr "{user} heeft {sharee} verwijderd van {space}"
|
||||
|
||||
#: pkg/service/response.go:29
|
||||
msgid "{user} renamed {oldResource} to {resource}"
|
||||
msgstr "{user} heeft {oldResource} hernoemd tot {resource}"
|
||||
|
||||
#: pkg/service/response.go:33
|
||||
msgid "{user} shared {resource} via link"
|
||||
msgstr "{user} heeft {resource} gedeeld via link"
|
||||
|
||||
#: pkg/service/response.go:30
|
||||
msgid "{user} shared {resource} with {sharee}"
|
||||
msgstr "{user} heeft {resource} gedeeld met {sharee}"
|
||||
|
||||
#: pkg/service/response.go:34
|
||||
msgid "{user} updated {field} for a link {token} on {resource}"
|
||||
msgstr "{user} heeft {field} bijgewerkt voor een link {token} op {resource}"
|
||||
|
||||
#: pkg/service/response.go:31
|
||||
msgid "{user} updated {field} for the {resource}"
|
||||
msgstr "{user} heeft {field} bijgewerkt voor {resource}"
|
||||
|
||||
#: pkg/service/response.go:25
|
||||
msgid "{user} updated {resource} in {folder}"
|
||||
msgstr "{user} heeft {resource} bijgewerkt in {folder}"
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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:
|
||||
# Savely Krasovsky, 2025
|
||||
# Анастасия Ванина, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-08 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Анастасия Ванина, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#: pkg/service/response.go:44
|
||||
msgid "description"
|
||||
msgstr "Описание"
|
||||
|
||||
#: pkg/service/response.go:43
|
||||
msgid "display name"
|
||||
msgstr "Отображаемое имя"
|
||||
|
||||
#: pkg/service/response.go:42
|
||||
msgid "expiration date"
|
||||
msgstr "Дата истечения срока"
|
||||
|
||||
#: pkg/service/response.go:41
|
||||
msgid "password"
|
||||
msgstr "Пароль"
|
||||
|
||||
#: pkg/service/response.go:40
|
||||
msgid "permission"
|
||||
msgstr "Разрешение"
|
||||
|
||||
#: pkg/service/response.go:39
|
||||
msgid "some field"
|
||||
msgstr "Какое-то поле"
|
||||
|
||||
#: pkg/service/response.go:26
|
||||
msgid "{resource} was downloaded via public link {token}"
|
||||
msgstr "{resource} был загружен через публичную ссылку {token}"
|
||||
|
||||
#: pkg/service/response.go:24
|
||||
msgid "{user} added {resource} to {folder}"
|
||||
msgstr "{user} добавил(-ла) {resource} в{folder}"
|
||||
|
||||
#: pkg/service/response.go:36
|
||||
msgid "{user} added {sharee} as member of {space}"
|
||||
msgstr "{user} добавил(-а) {sharee} как члена {space}"
|
||||
|
||||
#: pkg/service/response.go:27
|
||||
msgid "{user} deleted {resource} from {folder}"
|
||||
msgstr "{user} удалил(-а) {resource} из {folder}"
|
||||
|
||||
#: pkg/service/response.go:28
|
||||
msgid "{user} moved {resource} to {folder}"
|
||||
msgstr "{user} перенес(-ла) {resource} в {folder}"
|
||||
|
||||
#: pkg/service/response.go:35
|
||||
msgid "{user} removed link to {resource}"
|
||||
msgstr "{user} удалил(-а) ссылку на {resource}"
|
||||
|
||||
#: pkg/service/response.go:32
|
||||
msgid "{user} removed {sharee} from {resource}"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/response.go:37
|
||||
msgid "{user} removed {sharee} from {space}"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/response.go:29
|
||||
msgid "{user} renamed {oldResource} to {resource}"
|
||||
msgstr "{user} переименовал {oldResource} в {resource}"
|
||||
|
||||
#: pkg/service/response.go:33
|
||||
msgid "{user} shared {resource} via link"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/response.go:30
|
||||
msgid "{user} shared {resource} with {sharee}"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/response.go:34
|
||||
msgid "{user} updated {field} for a link {token} on {resource}"
|
||||
msgstr "{user} обновил(-а) {field} для ссылки {token} на {resource}"
|
||||
|
||||
#: pkg/service/response.go:31
|
||||
msgid "{user} updated {field} for the {resource}"
|
||||
msgstr "{user} обновил(-а) {field} у {resource}"
|
||||
|
||||
#: pkg/service/response.go:25
|
||||
msgid "{user} updated {resource} in {folder}"
|
||||
msgstr "{user} обновил(-а) {resource} в {folder}"
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-01-27 11:01+0100\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -84,6 +84,7 @@ type LDAPProvider struct {
|
||||
|
||||
type LDAPUserSchema struct {
|
||||
ID string `yaml:"id" env:"OC_LDAP_USER_SCHEMA_ID;AUTH_BASIC_LDAP_USER_SCHEMA_ID" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." introductionVersion:"1.0.0"`
|
||||
TenantID string `yaml:"tenant_id" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;AUTH_BASIC_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"`
|
||||
IDIsOctetString bool `yaml:"id_is_octet_string" env:"OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;AUTH_BASIC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING" desc:"Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is e.g. required when using the 'objectGUID' attribute of Active Directory for the user IDs." introductionVersion:"1.0.0"`
|
||||
Mail string `yaml:"mail" env:"OC_LDAP_USER_SCHEMA_MAIL;AUTH_BASIC_LDAP_USER_SCHEMA_MAIL" desc:"LDAP Attribute to use for the email address of users." introductionVersion:"1.0.0"`
|
||||
DisplayName string `yaml:"display_name" env:"OC_LDAP_USER_SCHEMA_DISPLAYNAME;AUTH_BASIC_LDAP_USER_SCHEMA_DISPLAYNAME" desc:"LDAP Attribute to use for the displayname of users." introductionVersion:"1.0.0"`
|
||||
|
||||
@@ -77,6 +77,7 @@ func ldapConfigFromString(cfg config.LDAPProvider) map[string]interface{} {
|
||||
"idp": cfg.IDP,
|
||||
"user_schema": map[string]interface{}{
|
||||
"id": cfg.UserSchema.ID,
|
||||
"tenantId": cfg.UserSchema.TenantID,
|
||||
"idIsOctetString": cfg.UserSchema.IDIsOctetString,
|
||||
"mail": cfg.UserSchema.Mail,
|
||||
"displayName": cfg.UserSchema.DisplayName,
|
||||
|
||||
@@ -73,6 +73,7 @@ type LDAP struct {
|
||||
UserNameAttribute string `yaml:"user_name_attribute" env:"OC_LDAP_USER_SCHEMA_USERNAME;GRAPH_LDAP_USER_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for username of users." introductionVersion:"1.0.0"`
|
||||
UserIDAttribute string `yaml:"user_id_attribute" env:"OC_LDAP_USER_SCHEMA_ID;GRAPH_LDAP_USER_UID_ATTRIBUTE" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." introductionVersion:"1.0.0"`
|
||||
UserIDIsOctetString bool `yaml:"user_id_is_octet_string" env:"OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;GRAPH_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING" desc:"Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is required when using the 'objectGUID' attribute of Active Directory for the user ID's." introductionVersion:"1.0.0"`
|
||||
UserTenantIDAttribute string `yaml:"user_tenant_id_attribute" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;GRAPH_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"`
|
||||
UserTypeAttribute string `yaml:"user_type_attribute" env:"OC_LDAP_USER_SCHEMA_USER_TYPE;GRAPH_LDAP_USER_TYPE_ATTRIBUTE" desc:"LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default is 'openCloudUserType'." introductionVersion:"1.0.0"`
|
||||
UserEnabledAttribute string `yaml:"user_enabled_attribute" env:"OC_LDAP_USER_ENABLED_ATTRIBUTE;GRAPH_USER_ENABLED_ATTRIBUTE" desc:"LDAP Attribute to use as a flag telling if the user is enabled or disabled." introductionVersion:"1.0.0"`
|
||||
DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OC_LDAP_DISABLE_USER_MECHANISM;GRAPH_DISABLE_USER_MECHANISM" desc:"An option to control the behavior for disabling users. Supported options are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API will add the user to the configured group for disabled users, if set to 'attribute' this will be done in the ldap user entry, if set to 'none' the disable request is not processed. Default is 'attribute'." introductionVersion:"1.0.0"`
|
||||
|
||||
@@ -96,6 +96,7 @@ func DefaultConfig() *config.Config {
|
||||
// FIXME: switch this to some more widely available attribute by default
|
||||
// ideally this needs to be constant for the lifetime of a users
|
||||
UserIDAttribute: "openCloudUUID",
|
||||
UserTenantIDAttribute: "",
|
||||
UserTypeAttribute: "openCloudUserType",
|
||||
UserEnabledAttribute: "openCloudUserEnabled",
|
||||
DisableUserMechanism: "attribute",
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
cs3group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
cs3user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -44,7 +44,7 @@ func (i *CS3) UpdateUser(ctx context.Context, nameOrID string, user libregraph.U
|
||||
}
|
||||
|
||||
// GetUser implements the Backend Interface.
|
||||
func (i *CS3) GetUser(ctx context.Context, userID string, _ *godata.GoDataRequest) (*libregraph.User, error) {
|
||||
func (i *CS3) GetUser(ctx context.Context, nameOrId string, _ *godata.GoDataRequest) (*libregraph.User, error) {
|
||||
logger := i.Logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "cs3").Msg("GetUser")
|
||||
gatewayClient, err := i.GatewaySelector.Next()
|
||||
@@ -53,22 +53,43 @@ func (i *CS3) GetUser(ctx context.Context, userID string, _ *godata.GoDataReques
|
||||
return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
|
||||
}
|
||||
|
||||
// Try to get the user by username first
|
||||
res, err := gatewayClient.GetUserByClaim(ctx, &cs3user.GetUserByClaimRequest{
|
||||
Claim: "userid", // FIXME add consts to reva
|
||||
Value: userID,
|
||||
Claim: "username", // FIXME add consts to reva
|
||||
Value: nameOrId,
|
||||
})
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
logger.Error().Str("backend", "cs3").Err(err).Str("userid", userID).Msg("error sending get user by claim id grpc request: transport error")
|
||||
logger.Error().Str("backend", "cs3").Err(err).Str("nameOrId", nameOrId).Msg("error sending get user by claim id grpc request: transport error")
|
||||
return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_OK:
|
||||
return CreateUserModelFromCS3(res.GetUser()), nil
|
||||
case res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND:
|
||||
// If the user was not found by username, try to get it by user ID
|
||||
default:
|
||||
logger.Debug().Str("backend", "cs3").Err(err).Str("nameOrId", nameOrId).Msg("error sending get user by claim id grpc request")
|
||||
return nil, errorcode.New(errorcode.GeneralException, res.GetStatus().GetMessage())
|
||||
|
||||
}
|
||||
|
||||
// If the user was not found by username, try to get it by user ID
|
||||
res, err = gatewayClient.GetUserByClaim(ctx, &cs3user.GetUserByClaimRequest{
|
||||
Claim: "userid", // FIXME add consts to reva
|
||||
Value: nameOrId,
|
||||
})
|
||||
switch {
|
||||
case err != nil:
|
||||
logger.Error().Str("backend", "cs3").Err(err).Str("nameOrId", nameOrId).Msg("error sending get user by claim id grpc request: transport error")
|
||||
return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
|
||||
case res.GetStatus().GetCode() != cs3rpc.Code_CODE_OK:
|
||||
if res.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
return nil, errorcode.New(errorcode.ItemNotFound, res.GetStatus().GetMessage())
|
||||
}
|
||||
logger.Debug().Str("backend", "cs3").Err(err).Str("userid", userID).Msg("error sending get user by claim id grpc request")
|
||||
logger.Debug().Str("backend", "cs3").Err(err).Str("nameOrId", nameOrId).Msg("error sending get user by claim id grpc request")
|
||||
return nil, errorcode.New(errorcode.GeneralException, res.GetStatus().GetMessage())
|
||||
}
|
||||
|
||||
return CreateUserModelFromCS3(res.GetUser()), nil
|
||||
}
|
||||
|
||||
@@ -167,7 +188,7 @@ func (i *CS3) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*lib
|
||||
|
||||
// CreateGroup implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) CreateGroup(ctx context.Context, group libregraph.Group) (*libregraph.Group, error) {
|
||||
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetGroup implements the Backend Interface.
|
||||
@@ -202,25 +223,25 @@ func (i *CS3) GetGroup(ctx context.Context, groupID string, queryParam url.Value
|
||||
|
||||
// DeleteGroup implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) DeleteGroup(ctx context.Context, id string) error {
|
||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// UpdateGroupName implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) UpdateGroupName(ctx context.Context, groupID string, groupName string) error {
|
||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) GetGroupMembers(ctx context.Context, groupID string, _ *godata.GoDataRequest) ([]*libregraph.User, error) {
|
||||
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// AddMembersToGroup implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) AddMembersToGroup(ctx context.Context, groupID string, memberID []string) error {
|
||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// RemoveMemberFromGroup implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) RemoveMemberFromGroup(ctx context.Context, groupID string, memberID string) error {
|
||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/libregraph/idm/pkg/ldapdn"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
|
||||
@@ -80,6 +81,7 @@ type LDAP struct {
|
||||
type userAttributeMap struct {
|
||||
displayName string
|
||||
id string
|
||||
tenantId string
|
||||
mail string
|
||||
userName string
|
||||
givenName string
|
||||
@@ -115,6 +117,7 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
|
||||
uam := userAttributeMap{
|
||||
displayName: config.UserDisplayNameAttribute,
|
||||
id: config.UserIDAttribute,
|
||||
tenantId: config.UserTenantIDAttribute,
|
||||
mail: config.UserEmailAttribute,
|
||||
userName: config.UserNameAttribute,
|
||||
accountEnabled: config.UserEnabledAttribute,
|
||||
@@ -614,7 +617,17 @@ func (i *LDAP) FilterUsers(ctx context.Context, oreq *godata.GoDataRequest, filt
|
||||
i.userAttributeMap.displayName, search,
|
||||
)
|
||||
}
|
||||
userFilter = fmt.Sprintf("(&%s(objectClass=%s)%s%s)", i.userFilter, i.userObjectClass, queryFilter, userFilter)
|
||||
|
||||
// apply tenant filter if applicable
|
||||
var tenantFilter string
|
||||
if i.userAttributeMap.tenantId != "" {
|
||||
currentUser, ok := ctxpkg.ContextGetUser(ctx)
|
||||
if ok && currentUser.Id.GetTenantId() != "" {
|
||||
tenantFilter = fmt.Sprintf("(%s=%s)", i.userAttributeMap.tenantId, ldap.EscapeFilter(currentUser.Id.GetTenantId()))
|
||||
}
|
||||
}
|
||||
|
||||
userFilter = fmt.Sprintf("(&%s(objectClass=%s)%s%s%s)", i.userFilter, i.userObjectClass, queryFilter, userFilter, tenantFilter)
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.userBaseDN, i.userScope, ldap.NeverDerefAliases, 0, 0, false,
|
||||
userFilter,
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
138
services/graph/pkg/l10n/locale/nl/LC_MESSAGES/graph.po
Normal file
138
services/graph/pkg/l10n/locale/nl/LC_MESSAGES/graph.po
Normal file
@@ -0,0 +1,138 @@
|
||||
# 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:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-05 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. UnifiedRole Editor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole EditorListGrants, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole SpaseEditor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole FileEditor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole FileEditorListGrants, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:116 pkg/unifiedrole/roles.go:122
|
||||
#: pkg/unifiedrole/roles.go:128 pkg/unifiedrole/roles.go:140
|
||||
#: pkg/unifiedrole/roles.go:146
|
||||
msgid "Can edit"
|
||||
msgstr "Kan bewerken"
|
||||
|
||||
#. UnifiedRole SpaseEditorWithoutVersions, Role DisplayName (resolves
|
||||
#. directly)
|
||||
#: pkg/unifiedrole/roles.go:134
|
||||
msgid "Can edit without versions"
|
||||
msgstr "Kan bewerken zonder versies"
|
||||
|
||||
#. UnifiedRole Manager, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:158
|
||||
msgid "Can manage"
|
||||
msgstr "Kan beheren"
|
||||
|
||||
#. UnifiedRole EditorLite, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:152
|
||||
msgid "Can upload"
|
||||
msgstr "Kan uploaden"
|
||||
|
||||
#. UnifiedRole Viewer, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole Viewer, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole SpaseViewer, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:98 pkg/unifiedrole/roles.go:104
|
||||
#: pkg/unifiedrole/roles.go:110
|
||||
msgid "Can view"
|
||||
msgstr "Kan weergeven"
|
||||
|
||||
#. UnifiedRole SecureViewer, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:164
|
||||
msgid "Can view (secure)"
|
||||
msgstr "Kan weergeven (beveiligd)"
|
||||
|
||||
#. UnifiedRole FullDenial, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:170
|
||||
msgid "Cannot access"
|
||||
msgstr "Heeft geen toegang"
|
||||
|
||||
#. UnifiedRole FullDenial, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:167
|
||||
msgid "Deny all access."
|
||||
msgstr "Alle toegang weigeren."
|
||||
|
||||
#. default description for new spaces
|
||||
#: pkg/service/v0/spacetemplates.go:32
|
||||
msgid "Here you can add a description for this Space."
|
||||
msgstr "Hier kun je een beschrijving voor deze ruimte toevoegen."
|
||||
|
||||
#. UnifiedRole Viewer, Role Description (resolves directly)
|
||||
#. UnifiedRole SpaceViewer, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:95 pkg/unifiedrole/roles.go:107
|
||||
msgid "View and download."
|
||||
msgstr "Weergeven en downloaden."
|
||||
|
||||
#. UnifiedRole SecureViewer, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:161
|
||||
msgid "View only documents, images and PDFs. Watermarks will be applied."
|
||||
msgstr ""
|
||||
"Alleen documenten, afbeeldingen en PDF's weergeven. Met toepassing van "
|
||||
"watermerken."
|
||||
|
||||
#. UnifiedRole FileEditor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:137
|
||||
msgid "View, download and edit."
|
||||
msgstr "Weergeven, downloaden en bewerken."
|
||||
|
||||
#. UnifiedRole ViewerListGrants, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:101
|
||||
msgid "View, download and show all invited people."
|
||||
msgstr "Weergeven, downloaden en alle genodigde personen zien."
|
||||
|
||||
#. UnifiedRole EditorLite, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:149
|
||||
msgid "View, download and upload."
|
||||
msgstr "Weergeven, downloaden en uploaden."
|
||||
|
||||
#. UnifiedRole FileEditorListGrants, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:143
|
||||
msgid "View, download, edit and show all invited people."
|
||||
msgstr "Weergeven, downloaden, bewerken en alle genodigde personen zien."
|
||||
|
||||
#. UnifiedRole Editor, Role Description (resolves directly)
|
||||
#. UnifiedRole SpaseEditorWithoutVersions, Role Description (resolves
|
||||
#. directly)
|
||||
#: pkg/unifiedrole/roles.go:113 pkg/unifiedrole/roles.go:131
|
||||
msgid "View, download, upload, edit, add and delete."
|
||||
msgstr "Weergeven, downloaden, uploaden, bewerken, toevoegen en verwijderen."
|
||||
|
||||
#. UnifiedRole Manager, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:155
|
||||
msgid "View, download, upload, edit, add, delete and manage members."
|
||||
msgstr ""
|
||||
"Weergeven, downloaden, uploaden, bewerken, toevoegen, verwijderen en leden "
|
||||
"beheren."
|
||||
|
||||
#. UnifiedRoleListGrants Editor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:119
|
||||
msgid "View, download, upload, edit, add, delete and show all invited people."
|
||||
msgstr ""
|
||||
"Weergeven, downloaden, uploaden, bewerken, toevoegen, verwijderen en alle "
|
||||
"genodigde personen zien."
|
||||
|
||||
#. UnifiedRole SpaseEditor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:125
|
||||
msgid "View, download, upload, edit, add, delete including the history."
|
||||
msgstr ""
|
||||
"Weergeven, downloaden, uploaden, bewerken, toevoegen, verwijderen, incl. "
|
||||
"geschiedenis."
|
||||
140
services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po
Normal file
140
services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po
Normal file
@@ -0,0 +1,140 @@
|
||||
# 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:
|
||||
# Savely Krasovsky, 2025
|
||||
# Lulufox, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-12 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#. UnifiedRole Editor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole EditorListGrants, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole SpaseEditor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole FileEditor, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole FileEditorListGrants, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:116 pkg/unifiedrole/roles.go:122
|
||||
#: pkg/unifiedrole/roles.go:128 pkg/unifiedrole/roles.go:140
|
||||
#: pkg/unifiedrole/roles.go:146
|
||||
msgid "Can edit"
|
||||
msgstr "Может редактировать"
|
||||
|
||||
#. UnifiedRole SpaseEditorWithoutVersions, Role DisplayName (resolves
|
||||
#. directly)
|
||||
#: pkg/unifiedrole/roles.go:134
|
||||
msgid "Can edit without versions"
|
||||
msgstr "Может редактировать без версионирования"
|
||||
|
||||
#. UnifiedRole Manager, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:158
|
||||
msgid "Can manage"
|
||||
msgstr "Может управлять"
|
||||
|
||||
#. UnifiedRole EditorLite, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:152
|
||||
msgid "Can upload"
|
||||
msgstr "Может загружать"
|
||||
|
||||
#. UnifiedRole Viewer, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole Viewer, Role DisplayName (resolves directly)
|
||||
#. UnifiedRole SpaseViewer, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:98 pkg/unifiedrole/roles.go:104
|
||||
#: pkg/unifiedrole/roles.go:110
|
||||
msgid "Can view"
|
||||
msgstr "Можно смотреть"
|
||||
|
||||
#. UnifiedRole SecureViewer, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:164
|
||||
msgid "Can view (secure)"
|
||||
msgstr "Можно смотреть (защищено)"
|
||||
|
||||
#. UnifiedRole FullDenial, Role DisplayName (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:170
|
||||
msgid "Cannot access"
|
||||
msgstr "Не имеет доступа"
|
||||
|
||||
#. UnifiedRole FullDenial, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:167
|
||||
msgid "Deny all access."
|
||||
msgstr "Отказать во всех доступах"
|
||||
|
||||
#. default description for new spaces
|
||||
#: pkg/service/v0/spacetemplates.go:32
|
||||
msgid "Here you can add a description for this Space."
|
||||
msgstr "Здесь вы можете добавить описание для Пространства"
|
||||
|
||||
#. UnifiedRole Viewer, Role Description (resolves directly)
|
||||
#. UnifiedRole SpaceViewer, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:95 pkg/unifiedrole/roles.go:107
|
||||
msgid "View and download."
|
||||
msgstr "Просмотреть и скачать"
|
||||
|
||||
#. UnifiedRole SecureViewer, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:161
|
||||
msgid "View only documents, images and PDFs. Watermarks will be applied."
|
||||
msgstr ""
|
||||
"Просмотреть только документы, изображения и файлы PDF. Будут присутствовать "
|
||||
"вотермарки."
|
||||
|
||||
#. UnifiedRole FileEditor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:137
|
||||
msgid "View, download and edit."
|
||||
msgstr "Просмотреть, скачать и редактировать."
|
||||
|
||||
#. UnifiedRole ViewerListGrants, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:101
|
||||
msgid "View, download and show all invited people."
|
||||
msgstr "Просмотреть, скачать и показать всех приглашенных людей."
|
||||
|
||||
#. UnifiedRole EditorLite, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:149
|
||||
msgid "View, download and upload."
|
||||
msgstr "Просмотреть, скачать и загрузить."
|
||||
|
||||
#. UnifiedRole FileEditorListGrants, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:143
|
||||
msgid "View, download, edit and show all invited people."
|
||||
msgstr ""
|
||||
"Просмотреть, скачать, отредактировать и показать всех приглашенных людей."
|
||||
|
||||
#. UnifiedRole Editor, Role Description (resolves directly)
|
||||
#. UnifiedRole SpaseEditorWithoutVersions, Role Description (resolves
|
||||
#. directly)
|
||||
#: pkg/unifiedrole/roles.go:113 pkg/unifiedrole/roles.go:131
|
||||
msgid "View, download, upload, edit, add and delete."
|
||||
msgstr "Просмотреть, скачать, загрузить, редактировать, добавить и удалить."
|
||||
|
||||
#. UnifiedRole Manager, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:155
|
||||
msgid "View, download, upload, edit, add, delete and manage members."
|
||||
msgstr ""
|
||||
"Просмотреть, скачать, загрузить, редактировать, добавить, удалить и "
|
||||
"управлять командой."
|
||||
|
||||
#. UnifiedRoleListGrants Editor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:119
|
||||
msgid "View, download, upload, edit, add, delete and show all invited people."
|
||||
msgstr ""
|
||||
"Просмотреть, скачать, загрузить, редактировать, добавить, удалить и показать"
|
||||
" всех приглашенных людей."
|
||||
|
||||
#. UnifiedRole SpaseEditor, Role Description (resolves directly)
|
||||
#: pkg/unifiedrole/roles.go:125
|
||||
msgid "View, download, upload, edit, add, delete including the history."
|
||||
msgstr ""
|
||||
"Просмотреть, скачать, загрузить, редактировать, добавить, удалить включая "
|
||||
"историю."
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
)
|
||||
@@ -17,9 +20,7 @@ func (g Graph) GetSharedByMe(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Debug().Msg("Calling GetRootDriveChildren")
|
||||
ctx := r.Context()
|
||||
|
||||
driveItems := make(driveItemsByResourceID)
|
||||
var err error
|
||||
driveItems, err = g.listUserShares(ctx, nil, driveItems)
|
||||
driveItems, err := g.listUserShares(ctx, nil, make(driveItemsByResourceID))
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
@@ -39,6 +40,37 @@ func (g Graph) GetSharedByMe(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
expand := r.URL.Query().Get("$expand")
|
||||
expandThumbnails := strings.Contains(expand, "thumbnails")
|
||||
if expandThumbnails {
|
||||
for k, item := range driveItems {
|
||||
mt := item.GetFile().MimeType
|
||||
if mt == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, match := thumbnail.SupportedMimeTypes[*mt]
|
||||
if match {
|
||||
baseUrl := fmt.Sprintf("%s/dav/spaces/%s?scalingup=0&preview=1&processor=thumbnail",
|
||||
g.config.Commons.OpenCloudURL,
|
||||
item.GetId())
|
||||
smallUrl := baseUrl + "&x=36&y=36"
|
||||
mediumUrl := baseUrl + "&x=48&y=48"
|
||||
largeUrl := baseUrl + "&x=96&y=96"
|
||||
|
||||
item.SetThumbnails([]libregraph.ThumbnailSet{
|
||||
{
|
||||
Small: &libregraph.Thumbnail{Url: &smallUrl},
|
||||
Medium: &libregraph.Thumbnail{Url: &mediumUrl},
|
||||
Large: &libregraph.Thumbnail{Url: &largeUrl},
|
||||
},
|
||||
})
|
||||
|
||||
driveItems[k] = item // assign modified item back to the map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := make([]libregraph.DriveItem, 0, len(driveItems))
|
||||
for _, v := range driveItems {
|
||||
res = append(res, v)
|
||||
|
||||
@@ -2,7 +2,9 @@ package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
@@ -10,12 +12,17 @@ import (
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
|
||||
)
|
||||
|
||||
// ListSharedWithMe lists the files shared with the current user.
|
||||
func (g Graph) ListSharedWithMe(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
driveItems, err := g.listSharedWithMe(ctx)
|
||||
|
||||
expand := r.URL.Query().Get("$expand")
|
||||
expandThumbnails := strings.Contains(expand, "thumbnails")
|
||||
|
||||
driveItems, err := g.listSharedWithMe(ctx, expandThumbnails)
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("listSharedWithMe failed")
|
||||
errorcode.RenderError(w, r, err)
|
||||
@@ -27,7 +34,7 @@ func (g Graph) ListSharedWithMe(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// listSharedWithMe is a helper function that lists the drive items shared with the current user.
|
||||
func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, error) {
|
||||
func (g Graph) listSharedWithMe(ctx context.Context, expandThumbnails bool) ([]libregraph.DriveItem, error) {
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("could not select next gateway client")
|
||||
@@ -59,5 +66,34 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er
|
||||
driveItems = append(driveItems, ocmDriveItems...)
|
||||
}
|
||||
|
||||
if expandThumbnails {
|
||||
for k, item := range driveItems {
|
||||
mt := item.GetFile().MimeType
|
||||
if mt == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, match := thumbnail.SupportedMimeTypes[*mt]
|
||||
if match {
|
||||
baseUrl := fmt.Sprintf("%s/dav/spaces/%s?scalingup=0&preview=1&processor=thumbnail",
|
||||
g.config.Commons.OpenCloudURL,
|
||||
item.RemoteItem.GetId())
|
||||
smallUrl := baseUrl + "&x=36&y=36"
|
||||
mediumUrl := baseUrl + "&x=48&y=48"
|
||||
largeUrl := baseUrl + "&x=96&y=96"
|
||||
|
||||
item.SetThumbnails([]libregraph.ThumbnailSet{
|
||||
{
|
||||
Small: &libregraph.Thumbnail{Url: &smallUrl},
|
||||
Medium: &libregraph.Thumbnail{Url: &mediumUrl},
|
||||
Large: &libregraph.Thumbnail{Url: &largeUrl},
|
||||
},
|
||||
})
|
||||
|
||||
driveItems[k] = item // assign modified item back to the map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return driveItems, err
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ displayName: Admin
|
||||
description: An admin for this OpenCloud instance.
|
||||
mail: admin@example.org
|
||||
openCloudUUID: {{ .ID }}
|
||||
openCloudTenantId: {{ .TenantID }}
|
||||
openCloudExternalIdentity: $ {{ .Issuer }} $ {{ .ID }}
|
||||
{{ else -}}
|
||||
dn: uid={{ .Name }},ou=sysusers,o=libregraph-idm
|
||||
|
||||
@@ -15,6 +15,7 @@ mail: alan@example.org
|
||||
openCloudUserEnabled: TRUE
|
||||
openCloudUUID: b1f74ec4-dd7e-11ef-a543-03775734d0f7
|
||||
openCloudExternalIdentity: $ {{.}} $ b1f74ec4-dd7e-11ef-a543-03775734d0f7
|
||||
openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw==
|
||||
|
||||
dn: uid=lynn,ou=users,o=libregraph-idm
|
||||
@@ -34,6 +35,7 @@ mail: lynn@example.org
|
||||
openCloudUserEnabled: TRUE
|
||||
openCloudUUID: 60708dda-e897-11ef-919f-bbb7437d6ec2
|
||||
openCloudExternalIdentity: $ {{.}} $ 60708dda-e897-11ef-919f-bbb7437d6ec2
|
||||
openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw==
|
||||
|
||||
dn: uid=mary,ou=users,o=libregraph-idm
|
||||
@@ -53,6 +55,7 @@ mail: mary@example.org
|
||||
openCloudUserEnabled: TRUE
|
||||
openCloudUUID: 056fc874-dd7f-11ef-ba84-af6fca4b7289
|
||||
openCloudExternalIdentity: $ {{.}} $ 056fc874-dd7f-11ef-ba84-af6fca4b7289
|
||||
openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw==
|
||||
|
||||
dn: uid=margaret,ou=users,o=libregraph-idm
|
||||
@@ -72,6 +75,7 @@ mail: margaret@example.org
|
||||
openCloudUserEnabled: TRUE
|
||||
openCloudUUID: 801abee4-dd7f-11ef-a324-83f55a754b62
|
||||
openCloudExternalIdentity: $ {{.}} $ 801abee4-dd7f-11ef-a324-83f55a754b62
|
||||
openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw==
|
||||
|
||||
dn: uid=dennis,ou=users,o=libregraph-idm
|
||||
@@ -91,6 +95,7 @@ mail: dennis@example.org
|
||||
openCloudUserEnabled: TRUE
|
||||
openCloudUUID: cd88bf9a-dd7f-11ef-a609-7f78deb2345f
|
||||
openCloudExternalIdentity: $ {{.}} $ cd88bf9a-dd7f-11ef-a609-7f78deb2345f
|
||||
openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw==
|
||||
|
||||
dn: cn=users,ou=groups,o=libregraph-idm
|
||||
|
||||
@@ -132,6 +132,7 @@ func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) erro
|
||||
Name string
|
||||
Password string
|
||||
ID string
|
||||
TenantID string
|
||||
Issuer string
|
||||
}
|
||||
|
||||
@@ -151,12 +152,16 @@ func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) erro
|
||||
}
|
||||
|
||||
if cfg.AdminUserID != "" {
|
||||
serviceUsers = append(serviceUsers, svcUser{
|
||||
adminUser := svcUser{
|
||||
Name: "admin",
|
||||
Password: cfg.ServiceUserPasswords.OCAdmin,
|
||||
ID: cfg.AdminUserID,
|
||||
Issuer: cfg.DemoUsersIssuerUrl,
|
||||
})
|
||||
}
|
||||
if cfg.CreateDemoUsers {
|
||||
adminUser.TenantID = "cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b"
|
||||
}
|
||||
serviceUsers = append(serviceUsers, adminUser)
|
||||
}
|
||||
|
||||
bdb := &ldbbolt.LdbBolt{}
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# 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:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-05 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. UnsharedSpace email template, resolves via {{ .CallToAction }}
|
||||
#: pkg/email/templates.go:65
|
||||
msgid "Click here to check it: {ShareLink}"
|
||||
msgstr "Klik hier om het te controleren: {ShareLink}"
|
||||
|
||||
#. ShareCreated email template, resolves via {{ .CallToAction }}
|
||||
#. SharedSpace email template, resolves via {{ .CallToAction }}
|
||||
#: pkg/email/templates.go:23 pkg/email/templates.go:50
|
||||
msgid "Click here to view it: {ShareLink}"
|
||||
msgstr "Klik hier om het te bekijken: {ShareLink}"
|
||||
|
||||
#. ShareCreated email template, resolves via {{ .Greeting }}
|
||||
#: pkg/email/templates.go:19
|
||||
msgid "Hello {ShareGrantee}"
|
||||
msgstr "Beste {ShareGrantee}"
|
||||
|
||||
#. ShareExpired email template, resolves via {{ .Greeting }}
|
||||
#: pkg/email/templates.go:32
|
||||
msgid "Hello {ShareGrantee},"
|
||||
msgstr "Beste {ShareGrantee},"
|
||||
|
||||
#. SharedSpace email template, resolves via {{ .Greeting }}
|
||||
#. UnsharedSpace email template, resolves via {{ .Greeting }}
|
||||
#. MembershipExpired email template, resolves via {{ .Greeting }}
|
||||
#: pkg/email/templates.go:46 pkg/email/templates.go:59
|
||||
#: pkg/email/templates.go:74
|
||||
msgid "Hello {SpaceGrantee},"
|
||||
msgstr "Beste {SpaceGrantee},"
|
||||
|
||||
#. Grouped email template, resolves via {{ .Greeting }}
|
||||
#: pkg/email/templates.go:118
|
||||
msgid "Hi {DisplayName},"
|
||||
msgstr "Hallo {DisplayName},"
|
||||
|
||||
#. ScienceMeshInviteTokenGenerated email template, resolves via {{ .Greeting
|
||||
#. }}
|
||||
#. ScienceMeshInviteTokenGeneratedWithoutShareLink email template, resolves
|
||||
#. via {{ .Greeting }}
|
||||
#: pkg/email/templates.go:87 pkg/email/templates.go:104
|
||||
msgid "Hi,"
|
||||
msgstr "Hallo,"
|
||||
|
||||
#. MembershipExpired email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:72
|
||||
msgid "Membership of '{SpaceName}' expired at {ExpiredAt}"
|
||||
msgstr "Lidmaatschap van '{SpaceName}' is verlopen op {ExpiredAt}"
|
||||
|
||||
#. Grouped email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:116
|
||||
msgid "Report"
|
||||
msgstr "Rapportage"
|
||||
|
||||
#. ScienceMeshInviteTokenGenerated email template, Subject field (resolves
|
||||
#. directly)
|
||||
#. ScienceMeshInviteTokenGeneratedWithoutShareLink email template, Subject
|
||||
#. field (resolves directly)
|
||||
#: pkg/email/templates.go:85 pkg/email/templates.go:102
|
||||
msgid "ScienceMesh: {InitiatorName} wants to collaborate with you"
|
||||
msgstr "ScienceMesh: {InitiatorName} wil met je samenwerken"
|
||||
|
||||
#. ShareExpired email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:30
|
||||
msgid "Share to '{ShareFolder}' expired at {ExpiredAt}"
|
||||
msgstr "Share '{ShareFolder}' is verlopen op {ExpiredAt}"
|
||||
|
||||
#. MembershipExpired email template, resolves via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:76
|
||||
msgid ""
|
||||
"Your membership of space {SpaceName} has expired at {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Even though this membership has expired you still might have access through other shares and/or space memberships"
|
||||
msgstr ""
|
||||
"Jouw lidmaatschap van ruimte {SpaceName} is verlopen op {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Hoewel dit lidmaatschap is verlopen, kun je mogelijk nog steeds toegang hebben via andere shares en/of ruimtelidmaatschappen."
|
||||
|
||||
#. ShareExpired email template, resolves via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:34
|
||||
msgid ""
|
||||
"Your share to {ShareFolder} has expired at {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Even though this share has been revoked you still might have access through other shares and/or space memberships."
|
||||
msgstr ""
|
||||
"Jouw toegang tot {ShareFolder} is verlopen op {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Hoewel de toegang is ingetrokken, kun je mogelijk nog steeds toegang hebben via andere shares en/of ruimtelidmaatschappen."
|
||||
|
||||
#. ScienceMeshInviteTokenGeneratedWithoutShareLink email template, resolves
|
||||
#. via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:106
|
||||
msgid ""
|
||||
"{ShareSharer} ({ShareSharerMail}) wants to start sharing collaboration resources with you.\n"
|
||||
"Please visit your federation settings and use the following details:\n"
|
||||
" Token: {Token}\n"
|
||||
" ProviderDomain: {ProviderDomain}"
|
||||
msgstr ""
|
||||
"{ShareSharer} ({ShareSharerMail}) wil samenwerkingsbronnen met je delen.\n"
|
||||
"Open je federatie-instellingen en gebruik de volgende gegevens.:\n"
|
||||
" Token: {Token}\n"
|
||||
" ProviderDomain: {ProviderDomain}"
|
||||
|
||||
#. ScienceMeshInviteTokenGenerated email template, resolves via {{
|
||||
#. .MessageBody }}
|
||||
#: pkg/email/templates.go:89
|
||||
msgid ""
|
||||
"{ShareSharer} ({ShareSharerMail}) wants to start sharing collaboration resources with you.\n"
|
||||
"To accept the invite, please visit the following URL:\n"
|
||||
"{ShareLink}\n"
|
||||
"\n"
|
||||
"Alternatively, you can visit your federation settings and use the following details:\n"
|
||||
" Token: {Token}\n"
|
||||
" ProviderDomain: {ProviderDomain}"
|
||||
msgstr ""
|
||||
"{ShareSharer} ({ShareSharerMail}) wil samenwerkingsbronnen met je delen.\n"
|
||||
"Open de volgende URL om de uitnodiging te accepteren:\n"
|
||||
"{ShareLink}\n"
|
||||
"\n"
|
||||
"Alternatief kun je je federatie-instellingen openen en de volgende gegevens gebruiken.:\n"
|
||||
" Token: {Token}\n"
|
||||
" ProviderDomain: {ProviderDomain}"
|
||||
|
||||
#. ShareCreated email template, resolves via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:21
|
||||
msgid "{ShareSharer} has shared \"{ShareFolder}\" with you."
|
||||
msgstr "{ShareSharer} heeft \"{ShareFolder}\" met jou gedeeld."
|
||||
|
||||
#. ShareCreated email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:17
|
||||
msgid "{ShareSharer} shared '{ShareFolder}' with you"
|
||||
msgstr "{ShareSharer} deelt '{ShareFolder}' met jou"
|
||||
|
||||
#. SharedSpace email template, resolves via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:48
|
||||
msgid "{SpaceSharer} has invited you to join \"{SpaceName}\"."
|
||||
msgstr "{SpaceSharer} heeft je uitgenodigd om deel te nemen in \"{SpaceName}\"."
|
||||
|
||||
#. UnsharedSpace email template, resolves via {{ .MessageBody }}
|
||||
#: pkg/email/templates.go:61
|
||||
msgid ""
|
||||
"{SpaceSharer} has removed you from \"{SpaceName}\".\n"
|
||||
"\n"
|
||||
"You might still have access through your other groups or direct membership."
|
||||
msgstr ""
|
||||
"{SpaceSharer} heeft jou verwijderd uit \"{SpaceName}\".\n"
|
||||
"\n"
|
||||
"Je hebt mogelijk nog steeds toegang via andere groepen of directe lidmaatschap."
|
||||
|
||||
#. SharedSpace email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:44
|
||||
msgid "{SpaceSharer} invited you to join {SpaceName}"
|
||||
msgstr "{SpaceSharer} heeft jou uitgenodigd om deel te nemen in {SpaceName}"
|
||||
|
||||
#. UnsharedSpace email template, Subject field (resolves directly)
|
||||
#: pkg/email/templates.go:57
|
||||
msgid "{SpaceSharer} removed you from {SpaceName}"
|
||||
msgstr "{SpaceSharer} heeft jou verwijderd uit {SpaceName}"
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,6 +11,50 @@ var (
|
||||
|
||||
// Subsystem defines the subsystem for the defines metrics.
|
||||
Subsystem = "postprocessing"
|
||||
|
||||
buildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "build_info",
|
||||
Help: "Build information",
|
||||
}, []string{"version"})
|
||||
eventsOutstandingAcks = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_outstanding_acks",
|
||||
Help: "Number of outstanding acks for events",
|
||||
})
|
||||
eventsUnprocessed = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_unprocessed",
|
||||
Help: "Number of unprocessed events",
|
||||
})
|
||||
eventsRedelivered = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_redelivered",
|
||||
Help: "Number of redelivered events",
|
||||
})
|
||||
inProgress = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "in_progress",
|
||||
Help: "Number of postprocessing events in progress",
|
||||
})
|
||||
finished = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "finished",
|
||||
Help: "Number of finished postprocessing events",
|
||||
}, []string{"status"})
|
||||
duration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "duration_seconds",
|
||||
Help: "Duration of postprocessing operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 600, 1200},
|
||||
}, []string{"status"})
|
||||
)
|
||||
|
||||
// Metrics defines the available metrics of this service.
|
||||
@@ -28,49 +72,13 @@ type Metrics struct {
|
||||
// New initializes the available metrics.
|
||||
func New() *Metrics {
|
||||
m := &Metrics{
|
||||
BuildInfo: promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "build_info",
|
||||
Help: "Build information",
|
||||
}, []string{"version"}),
|
||||
EventsOutstandingAcks: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_outstanding_acks",
|
||||
Help: "Number of outstanding acks for events",
|
||||
}),
|
||||
EventsUnprocessed: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_unprocessed",
|
||||
Help: "Number of unprocessed events",
|
||||
}),
|
||||
EventsRedelivered: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_redelivered",
|
||||
Help: "Number of redelivered events",
|
||||
}),
|
||||
InProgress: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "in_progress",
|
||||
Help: "Number of postprocessing events in progress",
|
||||
}),
|
||||
Finished: promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "finished",
|
||||
Help: "Number of finished postprocessing events",
|
||||
}, []string{"status"}),
|
||||
Duration: promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "duration_seconds",
|
||||
Help: "Duration of postprocessing operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 600, 1200},
|
||||
}, []string{"status"}),
|
||||
BuildInfo: buildInfo,
|
||||
EventsOutstandingAcks: eventsOutstandingAcks,
|
||||
EventsUnprocessed: eventsUnprocessed,
|
||||
EventsRedelivered: eventsRedelivered,
|
||||
InProgress: inProgress,
|
||||
Finished: finished,
|
||||
Duration: duration,
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
@@ -393,6 +393,7 @@ func monitorMetrics(stream raw.Stream, name string, m *metrics.Metrics, logger l
|
||||
info, err := consumer.Info(ctx)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("failed to get consumer")
|
||||
continue
|
||||
}
|
||||
|
||||
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
|
||||
|
||||
@@ -28,6 +28,7 @@ type Config struct {
|
||||
Engine Engine `yaml:"engine"`
|
||||
Extractor Extractor `yaml:"extractor"`
|
||||
ContentExtractionSizeLimit uint64 `yaml:"content_extraction_size_limit" env:"SEARCH_CONTENT_EXTRACTION_SIZE_LIMIT" desc:"Maximum file size in bytes that is allowed for content extraction." introductionVersion:"1.0.0"`
|
||||
BatchSize int `yaml:"batch_size" env:"SEARCH_BATCH_SIZE" desc:"The number of documents to process in a single batch. Defaults to 500." introductionVersion:"1.0.0"`
|
||||
|
||||
ServiceAccount ServiceAccount `yaml:"service_account"`
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ func DefaultConfig() *config.Config {
|
||||
AckWait: 1 * time.Minute,
|
||||
},
|
||||
ContentExtractionSizeLimit: 20 * 1024 * 1024, // Limit content extraction to <20MB files by default
|
||||
BatchSize: 500,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
@@ -20,10 +21,11 @@ import (
|
||||
"github.com/blevesearch/bleve/v2/mapping"
|
||||
"github.com/blevesearch/bleve/v2/search/query"
|
||||
storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
searchMessage "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/search/v0"
|
||||
@@ -32,10 +34,16 @@ import (
|
||||
searchQuery "github.com/opencloud-eu/opencloud/services/search/pkg/query"
|
||||
)
|
||||
|
||||
const _batchSize = 500
|
||||
|
||||
// Bleve represents a search engine which utilizes bleve to search and store resources.
|
||||
type Bleve struct {
|
||||
index bleve.Index
|
||||
queryCreator searchQuery.Creator[query.Query]
|
||||
batch *bleve.Batch
|
||||
batchSize int
|
||||
m sync.Mutex // batch operations in bleve are not thread-safe
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewBleveIndex returns a new bleve index
|
||||
@@ -60,10 +68,11 @@ func NewBleveIndex(root string) (bleve.Index, error) {
|
||||
}
|
||||
|
||||
// NewBleveEngine creates a new Bleve instance
|
||||
func NewBleveEngine(index bleve.Index, queryCreator searchQuery.Creator[query.Query]) *Bleve {
|
||||
func NewBleveEngine(index bleve.Index, queryCreator searchQuery.Creator[query.Query], log log.Logger) *Bleve {
|
||||
return &Bleve{
|
||||
index: index,
|
||||
queryCreator: queryCreator,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,8 +242,60 @@ func (b *Bleve) Search(ctx context.Context, sir *searchService.SearchIndexReques
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Bleve) StartBatch(batchSize int) error {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
if batchSize <= 0 {
|
||||
return errors.New("batch size must be greater than 0")
|
||||
}
|
||||
|
||||
if b.batch != nil {
|
||||
b.log.Debug().Msg("reusing another batch that has already been started")
|
||||
return nil
|
||||
}
|
||||
|
||||
b.log.Debug().Msg("Starting new batch")
|
||||
b.batch = b.index.NewBatch()
|
||||
b.batchSize = batchSize
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bleve) EndBatch() error {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
if b.batch == nil {
|
||||
return errors.New("no batch started")
|
||||
}
|
||||
|
||||
b.log.Debug().Int("size", b.batch.Size()).Msg("Ending batch")
|
||||
if err := b.index.Batch(b.batch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.batch = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upsert indexes or stores Resource data fields.
|
||||
func (b *Bleve) Upsert(id string, r Resource) error {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
if b.batch != nil {
|
||||
if err := b.batch.Index(id, r); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.batch.Size() >= b.batchSize {
|
||||
b.log.Debug().Int("size", b.batch.Size()).Msg("Committing batch")
|
||||
if err := b.index.Batch(b.batch); err != nil {
|
||||
return err
|
||||
}
|
||||
b.batch = b.index.NewBatch()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return b.index.Index(id, r)
|
||||
}
|
||||
|
||||
@@ -298,6 +359,19 @@ func (b *Bleve) Restore(id string) error {
|
||||
|
||||
// Purge removes a resource from the index, irreversible operation.
|
||||
func (b *Bleve) Purge(id string) error {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
if b.batch != nil {
|
||||
b.batch.Delete(id)
|
||||
if b.batch.Size() >= b.batchSize {
|
||||
if err := b.index.Batch(b.batch); err != nil {
|
||||
return err
|
||||
}
|
||||
b.batch = b.index.NewBatch()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return b.index.Delete(id)
|
||||
}
|
||||
|
||||
@@ -452,7 +526,7 @@ func (b *Bleve) updateEntity(id string, mutateFunc func(r *Resource)) (*Resource
|
||||
|
||||
mutateFunc(it)
|
||||
|
||||
return it, b.index.Index(it.ID, it)
|
||||
return it, b.Upsert(id, *it)
|
||||
}
|
||||
|
||||
func (b *Bleve) setDeleted(id string, deleted bool) error {
|
||||
@@ -468,6 +542,7 @@ func (b *Bleve) setDeleted(id string, deleted bool) error {
|
||||
bleve.NewQueryStringQuery("RootID:"+it.RootID),
|
||||
bleve.NewQueryStringQuery("Path:"+escapeQuery(it.Path+"/*")),
|
||||
)
|
||||
|
||||
bleveReq := bleve.NewSearchRequest(q)
|
||||
bleveReq.Size = math.MaxInt
|
||||
bleveReq.Fields = []string{"*"}
|
||||
@@ -476,6 +551,8 @@ func (b *Bleve) setDeleted(id string, deleted bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
b.StartBatch(_batchSize)
|
||||
defer b.EndBatch()
|
||||
for _, h := range res.Hits {
|
||||
_, err := b.updateEntity(h.ID, func(r *Resource) {
|
||||
r.Deleted = deleted
|
||||
@@ -484,6 +561,7 @@ func (b *Bleve) setDeleted(id string, deleted bool) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.EndBatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
searchmsg "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/search/v0"
|
||||
searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/content"
|
||||
@@ -53,6 +54,7 @@ var _ = Describe("Bleve", func() {
|
||||
rootResource engine.Resource
|
||||
parentResource engine.Resource
|
||||
childResource engine.Resource
|
||||
childResource2 engine.Resource
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
@@ -62,7 +64,7 @@ var _ = Describe("Bleve", func() {
|
||||
idx, err = bleveSearch.NewMemOnly(mapping)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
eng = engine.NewBleveEngine(idx, bleve.DefaultCreator)
|
||||
eng = engine.NewBleveEngine(idx, bleve.DefaultCreator, log.Logger{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
rootResource = engine.Resource{
|
||||
@@ -89,11 +91,20 @@ var _ = Describe("Bleve", func() {
|
||||
Type: uint64(sprovider.ResourceType_RESOURCE_TYPE_FILE),
|
||||
Document: content.Document{Name: "child.pdf"},
|
||||
}
|
||||
|
||||
childResource2 = engine.Resource{
|
||||
ID: "1$2!5",
|
||||
ParentID: parentResource.ID,
|
||||
RootID: rootResource.ID,
|
||||
Path: "./parent d!r/child2.pdf",
|
||||
Type: uint64(sprovider.ResourceType_RESOURCE_TYPE_FILE),
|
||||
Document: content.Document{Name: "child2.pdf"},
|
||||
}
|
||||
})
|
||||
|
||||
Describe("New", func() {
|
||||
It("returns a new index instance", func() {
|
||||
b := engine.NewBleveEngine(idx, bleve.DefaultCreator)
|
||||
b := engine.NewBleveEngine(idx, bleve.DefaultCreator, log.Logger{})
|
||||
Expect(b).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
@@ -486,6 +497,55 @@ var _ = Describe("Bleve", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("StartBatch", func() {
|
||||
It("starts a new batch", func() {
|
||||
err := eng.StartBatch(100)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = eng.Upsert(childResource.ID, childResource)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
count, err := idx.DocCount()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(count).To(Equal(uint64(0)))
|
||||
|
||||
err = eng.EndBatch()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
count, err = idx.DocCount()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(count).To(Equal(uint64(1)))
|
||||
|
||||
query := bleveSearch.NewMatchQuery("child.pdf")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(query))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits.Len()).To(Equal(1))
|
||||
})
|
||||
|
||||
It("doesn't overwrite batches that are already in progress", func() {
|
||||
err := eng.StartBatch(100)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = eng.Upsert(childResource.ID, childResource)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
count, err := idx.DocCount()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(count).To(Equal(uint64(0)))
|
||||
|
||||
err = eng.StartBatch(100)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = eng.Upsert(childResource2.ID, childResource2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(eng.EndBatch()).To(Succeed())
|
||||
count, err = idx.DocCount()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(count).To(Equal(uint64(2)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("File type specific metadata", func() {
|
||||
|
||||
Context("with audio metadata", func() {
|
||||
|
||||
@@ -23,6 +23,9 @@ type Engine interface {
|
||||
Restore(id string) error
|
||||
Purge(id string) error
|
||||
DocCount() (uint64, error)
|
||||
|
||||
StartBatch(batchSize int) error
|
||||
EndBatch() error
|
||||
}
|
||||
|
||||
// Resource is the entity that is stored in the index.
|
||||
|
||||
@@ -143,6 +143,50 @@ func (_c *Engine_DocCount_Call) RunAndReturn(run func() (uint64, error)) *Engine
|
||||
return _c
|
||||
}
|
||||
|
||||
// EndBatch provides a mock function for the type Engine
|
||||
func (_mock *Engine) EndBatch() error {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for EndBatch")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_EndBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EndBatch'
|
||||
type Engine_EndBatch_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// EndBatch is a helper method to define mock.On call
|
||||
func (_e *Engine_Expecter) EndBatch() *Engine_EndBatch_Call {
|
||||
return &Engine_EndBatch_Call{Call: _e.mock.On("EndBatch")}
|
||||
}
|
||||
|
||||
func (_c *Engine_EndBatch_Call) Run(run func()) *Engine_EndBatch_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_EndBatch_Call) Return(err error) *Engine_EndBatch_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_EndBatch_Call) RunAndReturn(run func() error) *Engine_EndBatch_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Move provides a mock function for the type Engine
|
||||
func (_mock *Engine) Move(id string, parentid string, target string) error {
|
||||
ret := _mock.Called(id, parentid, target)
|
||||
@@ -376,6 +420,57 @@ func (_c *Engine_Search_Call) RunAndReturn(run func(ctx context.Context, req *v0
|
||||
return _c
|
||||
}
|
||||
|
||||
// StartBatch provides a mock function for the type Engine
|
||||
func (_mock *Engine) StartBatch(batchSize int) error {
|
||||
ret := _mock.Called(batchSize)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for StartBatch")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(int) error); ok {
|
||||
r0 = returnFunc(batchSize)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_StartBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartBatch'
|
||||
type Engine_StartBatch_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// StartBatch is a helper method to define mock.On call
|
||||
// - batchSize int
|
||||
func (_e *Engine_Expecter) StartBatch(batchSize interface{}) *Engine_StartBatch_Call {
|
||||
return &Engine_StartBatch_Call{Call: _e.mock.On("StartBatch", batchSize)}
|
||||
}
|
||||
|
||||
func (_c *Engine_StartBatch_Call) Run(run func(batchSize int)) *Engine_StartBatch_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 int
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(int)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_StartBatch_Call) Return(err error) *Engine_StartBatch_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_StartBatch_Call) RunAndReturn(run func(batchSize int) error) *Engine_StartBatch_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Upsert provides a mock function for the type Engine
|
||||
func (_mock *Engine) Upsert(id string, r engine.Resource) error {
|
||||
ret := _mock.Called(id, r)
|
||||
|
||||
@@ -11,6 +11,45 @@ var (
|
||||
|
||||
// Subsystem defines the subsystem for the defines metrics.
|
||||
Subsystem = "search"
|
||||
|
||||
buildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "build_info",
|
||||
Help: "Build information",
|
||||
}, []string{"version"})
|
||||
eventsOutstandingAcks = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_outstanding_acks",
|
||||
Help: "Number of outstanding acks for events",
|
||||
})
|
||||
eventsUnprocessed = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_unprocessed",
|
||||
Help: "Number of unprocessed events",
|
||||
})
|
||||
eventsRedelivered = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_redelivered",
|
||||
Help: "Number of redelivered events",
|
||||
})
|
||||
searchDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "search_duration_seconds",
|
||||
Help: "Duration of search operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60},
|
||||
}, []string{"status"})
|
||||
indexDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "index_duration_seconds",
|
||||
Help: "Duration of indexing operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 600, 1200},
|
||||
}, []string{"status"})
|
||||
)
|
||||
|
||||
// Metrics defines the available metrics of this service.
|
||||
@@ -27,44 +66,12 @@ type Metrics struct {
|
||||
// New initializes the available metrics.
|
||||
func New() *Metrics {
|
||||
m := &Metrics{
|
||||
BuildInfo: promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "build_info",
|
||||
Help: "Build information",
|
||||
}, []string{"version"}),
|
||||
EventsOutstandingAcks: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_outstanding_acks",
|
||||
Help: "Number of outstanding acks for events",
|
||||
}),
|
||||
EventsUnprocessed: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_unprocessed",
|
||||
Help: "Number of unprocessed events",
|
||||
}),
|
||||
EventsRedelivered: promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "events_redelivered",
|
||||
Help: "Number of redelivered events",
|
||||
}),
|
||||
SearchDuration: promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "search_duration_seconds",
|
||||
Help: "Duration of search operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60},
|
||||
}, []string{"status"}),
|
||||
IndexDuration: promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "index_duration_seconds",
|
||||
Help: "Duration of indexing operations in seconds",
|
||||
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 600, 1200},
|
||||
}, []string{"status"}),
|
||||
BuildInfo: buildInfo,
|
||||
EventsOutstandingAcks: eventsOutstandingAcks,
|
||||
EventsUnprocessed: eventsUnprocessed,
|
||||
EventsRedelivered: eventsRedelivered,
|
||||
SearchDuration: searchDuration,
|
||||
IndexDuration: indexDuration,
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
@@ -24,7 +24,7 @@ type workItem struct {
|
||||
t *time.Timer
|
||||
timeout *time.Timer
|
||||
|
||||
trigger func()
|
||||
work func()
|
||||
}
|
||||
|
||||
type AckFunc func() error
|
||||
@@ -54,7 +54,8 @@ func (d *SpaceDebouncer) Debounce(id *provider.StorageSpaceId, ack AckFunc) {
|
||||
return
|
||||
}
|
||||
|
||||
trigger := func() {
|
||||
wi := &workItem{}
|
||||
wi.work = func() {
|
||||
if _, ok := d.inProgress.Load(id.OpaqueId); ok {
|
||||
// Reschedule this run for when the previous run has finished
|
||||
d.mutex.Lock()
|
||||
@@ -66,9 +67,12 @@ func (d *SpaceDebouncer) Debounce(id *provider.StorageSpaceId, ack AckFunc) {
|
||||
}
|
||||
|
||||
d.mutex.Lock()
|
||||
wi.timeout.Stop() // stop the timeout timer if it is running
|
||||
delete(d.pending, id.OpaqueId)
|
||||
d.inProgress.Store(id.OpaqueId, true)
|
||||
defer d.inProgress.Delete(id.OpaqueId)
|
||||
defer func() {
|
||||
d.inProgress.Delete(id.OpaqueId)
|
||||
}()
|
||||
d.mutex.Unlock() // release the lock early to allow other goroutines to debounce
|
||||
|
||||
d.f(id)
|
||||
@@ -80,16 +84,13 @@ func (d *SpaceDebouncer) Debounce(id *provider.StorageSpaceId, ack AckFunc) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
t := time.AfterFunc(d.after, trigger)
|
||||
wi.t = time.AfterFunc(d.after, wi.work)
|
||||
wi.timeout = time.AfterFunc(d.timeout, func() {
|
||||
d.log.Debug().Msg("timeout while waiting for space debouncer to finish")
|
||||
wi.t.Stop()
|
||||
wi.work()
|
||||
})
|
||||
|
||||
d.pending[id.OpaqueId] = &workItem{
|
||||
trigger: trigger,
|
||||
t: t,
|
||||
timeout: time.AfterFunc(d.timeout, func() {
|
||||
d.log.Debug().Msg("timeout while waiting for space debouncer to finish")
|
||||
t.Stop()
|
||||
trigger()
|
||||
}),
|
||||
}
|
||||
d.pending[id.OpaqueId] = wi
|
||||
|
||||
}
|
||||
|
||||
@@ -115,6 +115,26 @@ var _ = Describe("SpaceDebouncer", func() {
|
||||
}, "300ms").Should(Equal(1))
|
||||
})
|
||||
|
||||
It("doesn't run the timeout function if the work function has been called", func() {
|
||||
debouncer = search.NewSpaceDebouncer(100*time.Millisecond, 250*time.Millisecond, func(id *sprovider.StorageSpaceId) {
|
||||
if id.OpaqueId == "spaceid" {
|
||||
callCount.Add(1)
|
||||
}
|
||||
}, log.NewLogger())
|
||||
|
||||
// Initial call to start the timers
|
||||
debouncer.Debounce(spaceid, nil)
|
||||
|
||||
// Wait for the debounce timer to fire
|
||||
Eventually(func() int {
|
||||
return int(callCount.Load())
|
||||
}, "200ms").Should(Equal(1))
|
||||
|
||||
// The timeout function should not be called
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
Expect(int(callCount.Load())).To(Equal(1))
|
||||
})
|
||||
|
||||
It("calls the ack function when the debounce fires", func() {
|
||||
var ackCalled atomic.Bool
|
||||
ackFunc := func() error {
|
||||
|
||||
@@ -122,6 +122,7 @@ func monitorMetrics(stream raw.Stream, name string, m *metrics.Metrics, logger l
|
||||
info, err := consumer.Info(ctx)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("failed to get consumer")
|
||||
continue
|
||||
}
|
||||
|
||||
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
|
||||
|
||||
@@ -64,6 +64,8 @@ type Service struct {
|
||||
|
||||
serviceAccountID string
|
||||
serviceAccountSecret string
|
||||
|
||||
batchSize int
|
||||
}
|
||||
|
||||
var errSkipSpace error
|
||||
@@ -79,6 +81,8 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng e
|
||||
|
||||
serviceAccountID: cfg.ServiceAccount.ServiceAccountID,
|
||||
serviceAccountSecret: cfg.ServiceAccount.ServiceAccountSecret,
|
||||
|
||||
batchSize: cfg.BatchSize,
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -459,6 +463,12 @@ func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId) error {
|
||||
}()
|
||||
|
||||
w := walker.NewWalker(s.gatewaySelector)
|
||||
s.engine.StartBatch(s.batchSize)
|
||||
defer func() {
|
||||
if err := s.engine.EndBatch(); err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to end batch")
|
||||
}
|
||||
}()
|
||||
err = w.Walk(ownerCtx, &rootID, func(wd string, info *provider.ResourceInfo, err error) error {
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("error walking the tree")
|
||||
|
||||
@@ -122,6 +122,8 @@ var _ = Describe("Searchprovider", func() {
|
||||
User: user,
|
||||
}, nil)
|
||||
extractor.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
|
||||
indexClient.On("StartBatch", mock.Anything, mock.Anything).Return(nil)
|
||||
indexClient.On("EndBatch", mock.Anything, mock.Anything).Return(nil)
|
||||
indexClient.On("Upsert", mock.Anything, mock.Anything).Return(nil)
|
||||
indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{}, nil)
|
||||
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
|
||||
|
||||
@@ -53,7 +53,7 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, func(), error)
|
||||
_ = idx.Close()
|
||||
}
|
||||
|
||||
eng = engine.NewBleveEngine(idx, bleve.DefaultCreator)
|
||||
eng = engine.NewBleveEngine(idx, bleve.DefaultCreator, logger)
|
||||
default:
|
||||
return nil, teardown, fmt.Errorf("unknown search engine: %s", cfg.Engine.Type)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-02 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Toegevoegd als lid aan ruimte"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Dagelijks"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Interval van e-mails"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Bestand afgewezen"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instantaan"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nooit"
|
||||
|
||||
#. 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 "Stuur een melding wanneer ik als lid aan een ruimte ben toegevoegd"
|
||||
|
||||
#. 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 "Meld me wanneer ik ben verwijderd als lid van een ruimte"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Stuur een melding wanneer iemand iets met mij deelt"
|
||||
|
||||
#. 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 ""
|
||||
"Stuur een melding wanneer een bestand dat ik heb geüpload is afgewezen "
|
||||
"vanwege een virusinfectie of schending van het beleid."
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Stuur een melding wanneer een share met mij is ingetrokken"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Stuur een melding wanneer een share met mij is verlopen"
|
||||
|
||||
#. 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 "Stuur een melding wanneer een ruimte waarvan ik lid ben is verwijderd"
|
||||
|
||||
#. 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 ""
|
||||
"Stuur een melding wanner een ruimte waarvan ik lid ben is uitgeschakeld"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Stuur een melding waneer het lidmaatschap aan een ruimte is verlopen"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Verwijderd als lid van een ruimte"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Geselecteerde waarde:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Share verlopen"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Share ontvangen"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Share ingetrokken"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Ruimte verwijderd"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Ruimte uitgeschakeld"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Lidmaatschap van ruimte verlopen"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Wekelijks"
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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:
|
||||
# yellow sky, 2025
|
||||
# Анастасия Ванина, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-08 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Анастасия Ванина, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Добавлен как член пространства"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Ежедневно"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Интервал отправки электронных писем"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Файл отклонен"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Немедленно"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Никогда"
|
||||
|
||||
#. 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 "Уведомлять, когда меня добавляют в пространства"
|
||||
|
||||
#. 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 "Уведомлять, когда меня исключают из пространств"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr ""
|
||||
|
||||
#. 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 ""
|
||||
"Уведомлять, когда в загруженный мной файл был отклонен из-за проверки на "
|
||||
"вирусы"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr ""
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr ""
|
||||
|
||||
#. 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 "Уведомлять, когда пространство, в котором я состою, было удалено"
|
||||
|
||||
#. 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 "Уведомлять, когда пространство, в котором я состою, было отключено"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Уведомлять, когда мое членство в пространстве истекло"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Удален как участник пространства"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Выбранное значение:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr ""
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr ""
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr ""
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Пространство удалено"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Пространство отключено"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Членство в пространстве истекло"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Еженедельно"
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -24,7 +24,7 @@ type Config struct {
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"STORAGE_USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"1.0.0"`
|
||||
GracefulShutdownTimeout int `yaml:"graceful_shutdown_timeout" env:"STORAGE_USERS_GRACEFUL_SHUTDOWN_TIMEOUT" desc:"The number of seconds to wait for the 'storage-users' service to shutdown cleanly before exiting with an error that gets logged. Note: This setting is only applicable when running the 'storage-users' service as a standalone service. See the text description for more details." introductionVersion:"1.0.0"`
|
||||
|
||||
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'decomposed', Supported values are: 'decomposed', 'decomposeds3' and 'owncloudsql'. For backwards compatibility reasons it's also possible to use the 'ocis' and 's3ng' driver and configure them using the 'decomposed'/'decomposeds3' options. The 'decomposed' driver stores all data (blob and meta data) in an POSIX compliant volume. The 'decomposeds3' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket." introductionVersion:"1.0.0"`
|
||||
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'posix'. Supported values are: 'posix', 'decomposed', 'decomposeds3' and 'owncloudsql'. For backwards compatibility reasons it's also possible to use the 'ocis' and 's3ng' driver and configure them using the 'decomposed'/'decomposeds3' options. The 'posix' driver stores data directly on a POSIX-compliant filesystem. The 'decomposed' driver stores all data (blob and meta data) in a POSIX compliant volume. The 'decomposeds3' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket." introductionVersion:"1.0.0"`
|
||||
Drivers Drivers `yaml:"drivers"`
|
||||
DataServerURL string `yaml:"data_server_url" env:"STORAGE_USERS_DATA_SERVER_URL" desc:"URL of the data server, needs to be reachable by the data gateway provided by the frontend service or the user if directly exposed." introductionVersion:"1.0.0"`
|
||||
DataGatewayURL string `yaml:"data_gateway_url" env:"STORAGE_USERS_DATA_GATEWAY_URL" desc:"URL of the data gateway server" introductionVersion:"1.0.0"`
|
||||
|
||||
@@ -117,8 +117,13 @@ func (s Thumbnails) GetThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||
// TransferTokenValidator validates a transfer token
|
||||
func (s Thumbnails) TransferTokenValidator(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
logger := s.logger.SubloggerWithRequestID(r.Context())
|
||||
tokenString := r.Header.Get("Transfer-Token")
|
||||
if tokenString == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
logger := s.logger.SubloggerWithRequestID(r.Context())
|
||||
token, err := jwt.ParseWithClaims(tokenString, &tjwt.ThumbnailClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
# 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:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-05 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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: pkg/service/templates.go:39
|
||||
msgid "Access to Space {space} lost"
|
||||
msgstr "Toegang tot ruimte {space} kwijtgeraakt"
|
||||
|
||||
#: pkg/service/templates.go:54
|
||||
msgid "Access to {resource} expired"
|
||||
msgstr "Toegang tot {resource} verlopen"
|
||||
|
||||
#: pkg/service/templates.go:59
|
||||
msgid ""
|
||||
"Attention! The instance will be shut down and deprovisioned on {date}. "
|
||||
"Download all your data before that date as no access past that date is "
|
||||
"possible."
|
||||
msgstr ""
|
||||
"Let op! De instantie wordt op {date} afgesloten en afgeschakeld. Download "
|
||||
"voor deze datum al je gegevens, aangezien erna geen toegang meer mogelijk "
|
||||
"is."
|
||||
|
||||
#: pkg/service/templates.go:14
|
||||
msgid "File {resource} was deleted because it violates the policies"
|
||||
msgstr ""
|
||||
"Bestand {resource} is verwijderd vanwege scheding van de beleidsregels"
|
||||
|
||||
#: pkg/service/templates.go:58
|
||||
msgid "Instance will be shut down and deprovisioned"
|
||||
msgstr "De instantie zal worden afgesloten en afgeschakeld"
|
||||
|
||||
#: pkg/service/templates.go:38
|
||||
msgid "Membership expired"
|
||||
msgstr "Lidmaatschap verlopen"
|
||||
|
||||
#: pkg/service/templates.go:13
|
||||
msgid "Policies enforced"
|
||||
msgstr "Beleid afgedwongen"
|
||||
|
||||
#: pkg/service/templates.go:23
|
||||
msgid "Removed from Space"
|
||||
msgstr "Verwijderd uit ruimte"
|
||||
|
||||
#: pkg/service/templates.go:43
|
||||
msgid "Resource shared"
|
||||
msgstr "Hulpbron gedeeld"
|
||||
|
||||
#: pkg/service/templates.go:48
|
||||
msgid "Resource unshared"
|
||||
msgstr "Hulpbron niet gedeeld"
|
||||
|
||||
#: pkg/service/templates.go:53
|
||||
msgid "Share expired"
|
||||
msgstr "Share verlopen"
|
||||
|
||||
#: pkg/service/templates.go:33
|
||||
msgid "Space deleted"
|
||||
msgstr "Ruimte verwijderd"
|
||||
|
||||
#: pkg/service/templates.go:28
|
||||
msgid "Space disabled"
|
||||
msgstr "Ruimte uitgeschakeld"
|
||||
|
||||
#: pkg/service/templates.go:18
|
||||
msgid "Space shared"
|
||||
msgstr "Ruimte gedeeld"
|
||||
|
||||
#: pkg/service/templates.go:8
|
||||
msgid "Virus found"
|
||||
msgstr "Virus aangetroffen"
|
||||
|
||||
#: pkg/service/templates.go:9
|
||||
msgid "Virus found in {resource}. Upload not possible. Virus: {virus}"
|
||||
msgstr ""
|
||||
"Virus aangetroffen in {resource}. Uploaden niet mogelijk. Virus: {virus}"
|
||||
|
||||
#: pkg/service/templates.go:19
|
||||
msgid "{user} added you to Space {space}"
|
||||
msgstr "{user} heeft jou toegevoegd aan ruimte {space}"
|
||||
|
||||
#: pkg/service/templates.go:34
|
||||
msgid "{user} deleted Space {space}"
|
||||
msgstr "{user} heeft ruimte {space} verwijderd"
|
||||
|
||||
#: pkg/service/templates.go:29
|
||||
msgid "{user} disabled Space {space}"
|
||||
msgstr "{user} heeft ruimte {space} uitgeschakeld"
|
||||
|
||||
#: pkg/service/templates.go:24
|
||||
msgid "{user} removed you from Space {space}"
|
||||
msgstr "{user} heeft jou verwijderd uit ruimte {space}"
|
||||
|
||||
#: pkg/service/templates.go:44
|
||||
msgid "{user} shared {resource} with you"
|
||||
msgstr "{user} heeft {resource} met jou gedeeld"
|
||||
|
||||
#: pkg/service/templates.go:49
|
||||
msgid "{user} unshared {resource} with you"
|
||||
msgstr "{user} heeft het delen van {resource} met jou beëindigd"
|
||||
@@ -0,0 +1,116 @@
|
||||
# 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:
|
||||
# Анастасия Ванина, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-08-08 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Анастасия Ванина, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#: pkg/service/templates.go:39
|
||||
msgid "Access to Space {space} lost"
|
||||
msgstr "Доступ к пространству {space} потерян"
|
||||
|
||||
#: pkg/service/templates.go:54
|
||||
msgid "Access to {resource} expired"
|
||||
msgstr "Доступ к {resource} больше не действителен"
|
||||
|
||||
#: pkg/service/templates.go:59
|
||||
msgid ""
|
||||
"Attention! The instance will be shut down and deprovisioned on {date}. "
|
||||
"Download all your data before that date as no access past that date is "
|
||||
"possible."
|
||||
msgstr ""
|
||||
"Внимание! Система будет отключена {date}. Выгрузите все ваши данные до этой "
|
||||
"даты - после этого сделать это будет невозможно."
|
||||
|
||||
#: pkg/service/templates.go:14
|
||||
msgid "File {resource} was deleted because it violates the policies"
|
||||
msgstr "Файл {resource} был удален, потому что он нарушает правила"
|
||||
|
||||
#: pkg/service/templates.go:58
|
||||
msgid "Instance will be shut down and deprovisioned"
|
||||
msgstr "Система будет отключена"
|
||||
|
||||
#: pkg/service/templates.go:38
|
||||
msgid "Membership expired"
|
||||
msgstr "Членство истекло"
|
||||
|
||||
#: pkg/service/templates.go:13
|
||||
msgid "Policies enforced"
|
||||
msgstr "Правила применены"
|
||||
|
||||
#: pkg/service/templates.go:23
|
||||
msgid "Removed from Space"
|
||||
msgstr "Удалить из пространства"
|
||||
|
||||
#: pkg/service/templates.go:43
|
||||
msgid "Resource shared"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/templates.go:48
|
||||
msgid "Resource unshared"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/templates.go:53
|
||||
msgid "Share expired"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/templates.go:33
|
||||
msgid "Space deleted"
|
||||
msgstr "Пространство удалено"
|
||||
|
||||
#: pkg/service/templates.go:28
|
||||
msgid "Space disabled"
|
||||
msgstr "Пространство отключено"
|
||||
|
||||
#: pkg/service/templates.go:18
|
||||
msgid "Space shared"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/service/templates.go:8
|
||||
msgid "Virus found"
|
||||
msgstr "Найден вирус"
|
||||
|
||||
#: pkg/service/templates.go:9
|
||||
msgid "Virus found in {resource}. Upload not possible. Virus: {virus}"
|
||||
msgstr ""
|
||||
"В ресурсе {resource} обнаружен вирус. Загрузка невозможна. Вирус: {virus}"
|
||||
|
||||
#: pkg/service/templates.go:19
|
||||
msgid "{user} added you to Space {space}"
|
||||
msgstr "{user} добавил Вас в пространство {space}"
|
||||
|
||||
#: pkg/service/templates.go:34
|
||||
msgid "{user} deleted Space {space}"
|
||||
msgstr "{user} удалил(-ла) пространство {space}"
|
||||
|
||||
#: pkg/service/templates.go:29
|
||||
msgid "{user} disabled Space {space}"
|
||||
msgstr "{user} отключил пространство {space}"
|
||||
|
||||
#: pkg/service/templates.go:24
|
||||
msgid "{user} removed you from Space {space}"
|
||||
msgstr "{user} удалил вас из пространства {space}"
|
||||
|
||||
#: pkg/service/templates.go:44
|
||||
msgid "{user} shared {resource} with you"
|
||||
msgstr "{user} поделился(-лась) с вами {resource}"
|
||||
|
||||
#: pkg/service/templates.go:49
|
||||
msgid "{user} unshared {resource} with you"
|
||||
msgstr ""
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2025-07-09 14:37+0000\n"
|
||||
"POT-Creation-Date: 2025-07-30 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"
|
||||
|
||||
@@ -86,6 +86,7 @@ type LDAPDriver struct {
|
||||
|
||||
type LDAPUserSchema struct {
|
||||
ID string `yaml:"id" env:"OC_LDAP_USER_SCHEMA_ID;USERS_LDAP_USER_SCHEMA_ID" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." introductionVersion:"1.0.0"`
|
||||
TenantID string `yaml:"tenant_id" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;USERS_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"`
|
||||
IDIsOctetString bool `yaml:"id_is_octet_string" env:"OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;USERS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING" desc:"Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is e.g. required when using the 'objectGUID' attribute of Active Directory for the user ID's." introductionVersion:"1.0.0"`
|
||||
Mail string `yaml:"mail" env:"OC_LDAP_USER_SCHEMA_MAIL;USERS_LDAP_USER_SCHEMA_MAIL" desc:"LDAP Attribute to use for the email address of users." introductionVersion:"1.0.0"`
|
||||
DisplayName string `yaml:"display_name" env:"OC_LDAP_USER_SCHEMA_DISPLAYNAME;USERS_LDAP_USER_SCHEMA_DISPLAYNAME" desc:"LDAP Attribute to use for the displayname of users." introductionVersion:"1.0.0"`
|
||||
|
||||
@@ -79,6 +79,7 @@ func ldapConfigFromString(cfg config.LDAPDriver) map[string]interface{} {
|
||||
"idp": cfg.IDP,
|
||||
"user_schema": map[string]interface{}{
|
||||
"id": cfg.UserSchema.ID,
|
||||
"tenantId": cfg.UserSchema.TenantID,
|
||||
"idIsOctetString": cfg.UserSchema.IDIsOctetString,
|
||||
"mail": cfg.UserSchema.Mail,
|
||||
"displayName": cfg.UserSchema.DisplayName,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SHELL := bash
|
||||
NAME := web
|
||||
WEB_ASSETS_VERSION = v3.2.0
|
||||
WEB_ASSETS_VERSION = v3.3.0
|
||||
WEB_ASSETS_BRANCH = main
|
||||
|
||||
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
searchmsg "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/search/v0"
|
||||
searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
|
||||
"github.com/opencloud-eu/opencloud/services/webdav/pkg/constants"
|
||||
"github.com/opencloud-eu/opencloud/services/webdav/pkg/net"
|
||||
"github.com/opencloud-eu/opencloud/services/webdav/pkg/prop"
|
||||
@@ -195,9 +196,15 @@ func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg
|
||||
}
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:name", match.Entity.Name))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", match.Entity.LastModifiedTime.AsTime().Format(constants.RFC1123)))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", match.Entity.Permissions))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:highlights", match.Entity.Highlights))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
|
||||
_, isSupportedMimeType := thumbnail.SupportedMimeTypes[match.Entity.MimeType]
|
||||
if isSupportedMimeType {
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:has-preview", "1"))
|
||||
} else {
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:has-preview", "0"))
|
||||
}
|
||||
|
||||
t := tags.New(match.Entity.Tags...)
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:tags", t.AsList()))
|
||||
@@ -234,6 +241,15 @@ func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func hasPreview(md *provider.ResourceInfo, appendToOK func(p ...prop.PropertyXML)) {
|
||||
_, match := thumbnail.SupportedMimeTypes[md.MimeType]
|
||||
if match {
|
||||
appendToOK(prop.Escaped("oc:has-preview", "1"))
|
||||
} else {
|
||||
appendToOK(prop.Escaped("oc:has-preview", "0"))
|
||||
}
|
||||
}
|
||||
|
||||
type report struct {
|
||||
SearchFiles *reportSearchFiles
|
||||
// FilterFiles TODO add this for tag based search
|
||||
|
||||
@@ -344,8 +344,8 @@ class GraphHelper {
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $adminUser
|
||||
* @param string $adminPassword
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $searchTerm
|
||||
*
|
||||
* @return ResponseInterface
|
||||
@@ -353,16 +353,16 @@ class GraphHelper {
|
||||
public static function searchUser(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $adminUser,
|
||||
string $adminPassword,
|
||||
string $user,
|
||||
string $password,
|
||||
string $searchTerm
|
||||
): ResponseInterface {
|
||||
$url = self::getFullUrl($baseUrl, "users?\$search=$searchTerm");
|
||||
return HttpRequestHelper::get(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,13 @@ class OcHelper {
|
||||
return (\getenv("TEST_REVA") === "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUsingPreparedLdapUsers(): bool {
|
||||
return (\getenv("USE_PREPARED_LDAP_USERS") === "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|string false if no command given or the command as string
|
||||
*/
|
||||
|
||||
@@ -450,7 +450,7 @@ class SpacesContext implements Context {
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public function cleanDataAfterTests(): void {
|
||||
if (OcHelper::isTestingOnReva()) {
|
||||
if (OcHelper::isTestingOnReva() || OcHelper::isUsingPreparedLdapUsers()) {
|
||||
return;
|
||||
}
|
||||
$this->deleteAllProjectSpaces();
|
||||
|
||||
@@ -442,6 +442,13 @@ default:
|
||||
- AuthAppContext:
|
||||
- CliContext:
|
||||
- OcConfigContext:
|
||||
|
||||
apiTenancy:
|
||||
paths:
|
||||
- "%paths.base%/../features/apiTenancy"
|
||||
context: *common_ldap_suite_context
|
||||
contexts:
|
||||
- FeatureContext: *common_feature_context_params
|
||||
|
||||
cliCommands:
|
||||
paths:
|
||||
|
||||
77
tests/acceptance/features/apiTenancy/mutltiTenancy.feature
Normal file
77
tests/acceptance/features/apiTenancy/mutltiTenancy.feature
Normal file
@@ -0,0 +1,77 @@
|
||||
Feature: Multi-tenancy
|
||||
I want to make sure that users from different tenants are isolated from each other,
|
||||
so that each tenant's data and users remain private and secure.
|
||||
|
||||
Note:
|
||||
All users are managed via LDAP and are assumed to exist.
|
||||
Tests will use existing users without creating or deleting them.
|
||||
|
||||
Prepared LDAP users:
|
||||
| user | tenant | group |
|
||||
|-------|----------|----------------------|
|
||||
| alice | tenant-1 | new-features-lovers |
|
||||
| brian | tenant-1 | - |
|
||||
| carol | tenant-2 | - |
|
||||
| david | tenant-2 | release-lover |
|
||||
|
||||
|
||||
Scenario: users from the same tenant can see each other
|
||||
When user "Brian" searches for user "ali" using Graph API
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["value"],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"displayName",
|
||||
"id",
|
||||
"onPremisesSamAccountName",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"const": "Alice Hansen"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||
},
|
||||
"onPremisesSamAccountName": {
|
||||
"const": ""
|
||||
},
|
||||
"userType": {
|
||||
"const": "Member"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: users from different tenants cannot see each other
|
||||
When user "David" searches for user "brian" using Graph API
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["value"],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
25
tests/config/woodpecker/ldap/10_base.ldif
Normal file
25
tests/config/woodpecker/ldap/10_base.ldif
Normal file
@@ -0,0 +1,25 @@
|
||||
dn: dc=opencloud,dc=eu
|
||||
objectClass: organization
|
||||
objectClass: dcObject
|
||||
dc: opencloud
|
||||
o: openCloud
|
||||
|
||||
dn: ou=users,dc=opencloud,dc=eu
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
|
||||
dn: cn=admin,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: person
|
||||
cn: admin
|
||||
sn: admin
|
||||
uid: ldapadmin
|
||||
departmentNumber: {{ .TenantID }}
|
||||
|
||||
dn: ou=groups,dc=opencloud,dc=eu
|
||||
objectClass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: ou=custom,ou=groups,dc=opencloud,dc=eu
|
||||
objectClass: organizationalUnit
|
||||
ou: custom
|
||||
74
tests/config/woodpecker/ldap/20_users.ldif
Normal file
74
tests/config/woodpecker/ldap/20_users.ldif
Normal file
@@ -0,0 +1,74 @@
|
||||
dn: uid=alice,ou=users,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: alice
|
||||
givenName: Alice
|
||||
sn: Hansen
|
||||
cn: alice
|
||||
displayName: Alice Hansen
|
||||
description: Senior DevOps engineer responsible for cloud infrastructure automation.
|
||||
mail: alice@example.org
|
||||
departmentNumber: tenant-1
|
||||
userPassword:: e1NTSEF9eGY5OXlPOXVxSVJ5eTFxdFhQYVYxTnd6WE4wUWRReVU=
|
||||
|
||||
dn: uid=brian,ou=users,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: brian
|
||||
givenName: Brian
|
||||
sn: Murphy
|
||||
cn: brian
|
||||
displayName: Brian Murphy
|
||||
description: IT support specialist focused on end-user systems and service desk operations.
|
||||
mail: brian@example.org
|
||||
departmentNumber: tenant-1
|
||||
userPassword:: e1NTSEF9Y0xpdnEzdUxzNzB3SjU0R1dmR0EybndxbUZoRmNoOXQ=
|
||||
|
||||
dn: uid=carol,ou=users,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: carol
|
||||
givenName: Carol
|
||||
sn: King
|
||||
cn: carol
|
||||
displayName: Carol King
|
||||
description: Project manager leading enterprise IT solutions and digital transformation projects.
|
||||
mail: carol@example.org
|
||||
departmentNumber: tenant-2
|
||||
userPassword:: e1NTSEF9bWFiT2FyNEE4UWlJUm1Pb2JhNm1pYm1QMjQraDkzSEw=
|
||||
|
||||
dn: uid=david,ou=users,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: david
|
||||
givenName: David
|
||||
sn: Lopez
|
||||
cn: david
|
||||
displayName: David Lopez
|
||||
description: Systems architect working on scalable backend services and platform reliability.
|
||||
mail: david@example.org
|
||||
departmentNumber: tenant-2
|
||||
userPassword:: e1NTSEF9MEh2a3J0UTVONmZNSUhyL1NlWVQvclBYTjg0bi9SYlc=
|
||||
|
||||
dn: uid=admin,ou=users,dc=opencloud,dc=eu
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: admin
|
||||
givenName: Admin
|
||||
sn: Admin
|
||||
cn: Admin
|
||||
displayName: Admin
|
||||
description: System administrator
|
||||
mail: admin@example.org
|
||||
departmentNumber: tenant-1
|
||||
userPassword:: e1NTSEF9bFU2dDRHSC9Cb28wV2lnM1A0SVAzQTIyWE9aL2pCa1M=
|
||||
13
tests/config/woodpecker/ldap/30_groups.ldif
Normal file
13
tests/config/woodpecker/ldap/30_groups.ldif
Normal file
@@ -0,0 +1,13 @@
|
||||
dn: cn=new-features-lovers,ou=groups,dc=opencloud,dc=eu
|
||||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
cn: new-features-lovers
|
||||
description: New features lovers
|
||||
member: uid=alice,ou=users,dc=opencloud,dc=eu
|
||||
|
||||
dn: cn=release-lovers,ou=groups,dc=opencloud,dc=eu
|
||||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
cn: release-lovers
|
||||
description: Release lovers
|
||||
member: uid=david,ou=users,dc=opencloud,dc=eu
|
||||
42
tests/config/woodpecker/ldap/docker-entrypoint-override.sh
Normal file
42
tests/config/woodpecker/ldap/docker-entrypoint-override.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
printenv
|
||||
|
||||
if [ ! -f /opt/bitnami/openldap/share/openldap.key ]
|
||||
then
|
||||
openssl req -x509 -newkey rsa:4096 -keyout /opt/bitnami/openldap/share/openldap.key -out /opt/bitnami/openldap/share/openldap.crt -sha256 -days 365 -batch -nodes
|
||||
fi
|
||||
|
||||
mkdir -p /opt/bitnami/openldap/ldifs
|
||||
|
||||
if [ -d "/tmp/ldif-files" ]; then
|
||||
cp /tmp/ldif-files/*.ldif /opt/bitnami/openldap/ldifs/
|
||||
fi
|
||||
|
||||
/opt/bitnami/scripts/openldap/entrypoint.sh "$@" &
|
||||
ENTRYPOINT_PID=$!
|
||||
|
||||
echo "Waiting for LDAP server to start..."
|
||||
while ! ldapsearch -x -H ldap://localhost:1389 -D "cn=admin,dc=opencloud,dc=eu" -w admin -b "dc=opencloud,dc=eu" > /dev/null 2>&1; do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "LDAP server is running, importing LDIF files..."
|
||||
|
||||
if [ -f "/opt/bitnami/openldap/ldifs/10_base.ldif" ]; then
|
||||
echo "Importing 10_base.ldif..."
|
||||
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=opencloud,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/10_base.ldif
|
||||
fi
|
||||
|
||||
if [ -f "/opt/bitnami/openldap/ldifs/20_users.ldif" ]; then
|
||||
echo "Importing 20_users.ldif..."
|
||||
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=opencloud,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/20_users.ldif
|
||||
fi
|
||||
|
||||
if [ -f "/opt/bitnami/openldap/ldifs/30_groups.ldif" ]; then
|
||||
echo "Importing 30_groups.ldif..."
|
||||
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=opencloud,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/30_groups.ldif
|
||||
fi
|
||||
|
||||
echo "LDIF import completed!"
|
||||
|
||||
wait $ENTRYPOINT_PID
|
||||
131
vendor/github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1/resources.pb.go
generated
vendored
131
vendor/github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1/resources.pb.go
generated
vendored
@@ -132,6 +132,10 @@ type UserId struct {
|
||||
// REQUIRED.
|
||||
// The type of user.
|
||||
Type UserType `protobuf:"varint,3,opt,name=type,proto3,enum=cs3.identity.user.v1beta1.UserType" json:"type,omitempty"`
|
||||
// OPTIONAL.
|
||||
// The tenant id of the user, if applicable.
|
||||
// This is used to identify users in multi-tenant systems.
|
||||
TenantId string `protobuf:"bytes,4,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *UserId) Reset() {
|
||||
@@ -187,6 +191,13 @@ func (x *UserId) GetType() UserType {
|
||||
return UserType_USER_TYPE_INVALID
|
||||
}
|
||||
|
||||
func (x *UserId) GetTenantId() string {
|
||||
if x != nil {
|
||||
return x.TenantId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Represents a user of the system.
|
||||
type User struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -326,65 +337,67 @@ var file_cs3_identity_user_v1beta1_resources_proto_rawDesc = []byte{
|
||||
0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
|
||||
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1d, 0x63, 0x73, 0x33, 0x2f, 0x74, 0x79, 0x70, 0x65,
|
||||
0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x70, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x69, 0x64, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64,
|
||||
0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x49, 0x64, 0x12, 0x37,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x63,
|
||||
0x73, 0x33, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70,
|
||||
0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xba, 0x02, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72,
|
||||
0x12, 0x31, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63,
|
||||
0x73, 0x33, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52,
|
||||
0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d,
|
||||
0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x69,
|
||||
0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x61, 0x69, 0x6c,
|
||||
0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70,
|
||||
0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67,
|
||||
0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x18, 0x07, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x73, 0x33, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,
|
||||
0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x52, 0x06,
|
||||
0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x69, 0x64, 0x5f, 0x6e, 0x75,
|
||||
0x6d, 0x62, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x69, 0x64, 0x4e,
|
||||
0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x69, 0x64, 0x5f, 0x6e, 0x75, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x67, 0x69, 0x64, 0x4e, 0x75,
|
||||
0x6d, 0x62, 0x65, 0x72, 0x2a, 0xe7, 0x01, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70,
|
||||
0x65, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49,
|
||||
0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52,
|
||||
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12,
|
||||
0x17, 0x0a, 0x13, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x43,
|
||||
0x4f, 0x4e, 0x44, 0x41, 0x52, 0x59, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52,
|
||||
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x12,
|
||||
0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x50,
|
||||
0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x53,
|
||||
0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x10, 0x05, 0x12,
|
||||
0x17, 0x0a, 0x13, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x45, 0x44,
|
||||
0x45, 0x52, 0x41, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52,
|
||||
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x57, 0x45, 0x49, 0x47, 0x48,
|
||||
0x54, 0x10, 0x07, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45,
|
||||
0x5f, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x08, 0x42, 0xfa,
|
||||
0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x73, 0x33, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
|
||||
0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x50, 0x01, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||
0x73, 0x33, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x6f, 0x2d, 0x63, 0x73, 0x33, 0x61, 0x70, 0x69, 0x73,
|
||||
0x2f, 0x63, 0x73, 0x33, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76,
|
||||
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x49, 0x55, 0xaa, 0x02, 0x19, 0x43,
|
||||
0x73, 0x33, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x19, 0x43, 0x73, 0x33, 0x5c, 0x49,
|
||||
0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x62,
|
||||
0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x25, 0x43, 0x73, 0x33, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x74, 0x79, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
|
||||
0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x43,
|
||||
0x73, 0x33, 0x3a, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x55, 0x73,
|
||||
0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x69,
|
||||
0x64, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x49, 0x64, 0x12,
|
||||
0x37, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e,
|
||||
0x63, 0x73, 0x33, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65,
|
||||
0x72, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79,
|
||||
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e,
|
||||
0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xba, 0x02, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x31,
|
||||
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x73, 0x33,
|
||||
0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
|
||||
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x02, 0x69,
|
||||
0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x61, 0x69,
|
||||
0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69,
|
||||
0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x61, 0x69, 0x6c, 0x56, 0x65,
|
||||
0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
|
||||
0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69,
|
||||
0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70,
|
||||
0x73, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x19, 0x2e, 0x63, 0x73, 0x33, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31,
|
||||
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x52, 0x06, 0x6f, 0x70,
|
||||
0x61, 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x69, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x69, 0x64, 0x4e, 0x75, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x69, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65,
|
||||
0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x67, 0x69, 0x64, 0x4e, 0x75, 0x6d, 0x62,
|
||||
0x65, 0x72, 0x2a, 0xe7, 0x01, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56,
|
||||
0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54,
|
||||
0x59, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a,
|
||||
0x13, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x43, 0x4f, 0x4e,
|
||||
0x44, 0x41, 0x52, 0x59, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54,
|
||||
0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x12, 0x19, 0x0a,
|
||||
0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49,
|
||||
0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x53, 0x45, 0x52,
|
||||
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x10, 0x05, 0x12, 0x17, 0x0a,
|
||||
0x13, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x45, 0x44, 0x45, 0x52,
|
||||
0x41, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54,
|
||||
0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x57, 0x45, 0x49, 0x47, 0x48, 0x54, 0x10,
|
||||
0x07, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53,
|
||||
0x50, 0x41, 0x43, 0x45, 0x5f, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x08, 0x42, 0xfa, 0x01, 0x0a,
|
||||
0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x73, 0x33, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
|
||||
0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x0e,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
|
||||
0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x73, 0x33,
|
||||
0x6f, 0x72, 0x67, 0x2f, 0x67, 0x6f, 0x2d, 0x63, 0x73, 0x33, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x63,
|
||||
0x73, 0x33, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62,
|
||||
0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x49, 0x55, 0xaa, 0x02, 0x19, 0x43, 0x73, 0x33,
|
||||
0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56,
|
||||
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x19, 0x43, 0x73, 0x33, 0x5c, 0x49, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74,
|
||||
0x61, 0x31, 0xe2, 0x02, 0x25, 0x43, 0x73, 0x33, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
|
||||
0x79, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47,
|
||||
0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x43, 0x73, 0x33,
|
||||
0x3a, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x55, 0x73, 0x65, 0x72,
|
||||
0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
20
vendor/github.com/opencloud-eu/libre-graph-api-go/api_me_drive.go
generated
vendored
20
vendor/github.com/opencloud-eu/libre-graph-api-go/api_me_drive.go
generated
vendored
@@ -130,6 +130,13 @@ func (a *MeDriveApiService) GetHomeExecute(r ApiGetHomeRequest) (*Drive, *http.R
|
||||
type ApiListSharedByMeRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeDriveApiService
|
||||
expand *[]string
|
||||
}
|
||||
|
||||
// Expand related entities
|
||||
func (r ApiListSharedByMeRequest) Expand(expand []string) ApiListSharedByMeRequest {
|
||||
r.expand = &expand
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiListSharedByMeRequest) Execute() (*CollectionOfDriveItems1, *http.Response, error) {
|
||||
@@ -173,6 +180,9 @@ func (a *MeDriveApiService) ListSharedByMeExecute(r ApiListSharedByMeRequest) (*
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.expand != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "$expand", r.expand, "form", "csv")
|
||||
}
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
@@ -238,6 +248,13 @@ func (a *MeDriveApiService) ListSharedByMeExecute(r ApiListSharedByMeRequest) (*
|
||||
type ApiListSharedWithMeRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeDriveApiService
|
||||
expand *[]string
|
||||
}
|
||||
|
||||
// Expand related entities
|
||||
func (r ApiListSharedWithMeRequest) Expand(expand []string) ApiListSharedWithMeRequest {
|
||||
r.expand = &expand
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiListSharedWithMeRequest) Execute() (*CollectionOfDriveItems1, *http.Response, error) {
|
||||
@@ -281,6 +298,9 @@ func (a *MeDriveApiService) ListSharedWithMeExecute(r ApiListSharedWithMeRequest
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.expand != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "$expand", r.expand, "form", "csv")
|
||||
}
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
|
||||
@@ -24,18 +24,20 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/plugin"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/user"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/user/manager/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -172,7 +174,9 @@ func (s *service) GetUserByClaim(ctx context.Context, req *userpb.GetUserByClaim
|
||||
}
|
||||
|
||||
func (s *service) FindUsers(ctx context.Context, req *userpb.FindUsersRequest) (*userpb.FindUsersResponse, error) {
|
||||
users, err := s.usermgr.FindUsers(ctx, req.Filter, req.SkipFetchingUserGroups)
|
||||
currentUser := revactx.ContextMustGetUser(ctx)
|
||||
|
||||
users, err := s.usermgr.FindUsers(ctx, req.Filter, currentUser.Id.GetTenantId(), req.SkipFetchingUserGroups)
|
||||
if err != nil {
|
||||
res := &userpb.FindUsersResponse{
|
||||
Status: status.NewInternal(ctx, "error finding users"),
|
||||
|
||||
@@ -166,7 +166,6 @@ func (s *service) isPathAllowed(path string) bool {
|
||||
func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShareRequest) (*collaboration.CreateShareResponse, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
user := ctxpkg.ContextMustGetUser(ctx)
|
||||
|
||||
// Grants must not allow grant permissions
|
||||
if HasGrantPermissions(req.GetGrant().GetPermissions().GetPermissions()) {
|
||||
return &collaboration.CreateShareResponse{
|
||||
@@ -174,6 +173,17 @@ func (s *service) CreateShare(ctx context.Context, req *collaboration.CreateShar
|
||||
}, nil
|
||||
}
|
||||
|
||||
// check if the grantee is a user or group
|
||||
if req.GetGrant().GetGrantee().GetType() == provider.GranteeType_GRANTEE_TYPE_USER {
|
||||
// check if the tenantId of the user matches the tenantId of the target user
|
||||
if user.GetId().GetTenantId() != req.GetGrant().GetGrantee().GetUserId().GetTenantId() {
|
||||
log.Warn().Msg("user tenantId does not match the target user tenantId, this is not supported yet")
|
||||
return &collaboration.CreateShareResponse{
|
||||
Status: status.NewPermissionDenied(ctx, nil, "user tenantId does not match the target user tenantId"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
6
vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go
generated
vendored
6
vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go
generated
vendored
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
utils "github.com/opencloud-eu/reva/v2/pkg/cbox/utils"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/user"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/user/manager/registry"
|
||||
"github.com/pkg/errors"
|
||||
@@ -256,7 +257,10 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
if tenantID != "" {
|
||||
return nil, errtypes.NotSupported("tenant filter not supported in rest user manager")
|
||||
}
|
||||
|
||||
// Look at namespaces filters. If the query starts with:
|
||||
// "a" => look into primary/secondary/service accounts
|
||||
|
||||
2
vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/role.go
generated
vendored
2
vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/role.go
generated
vendored
@@ -183,6 +183,8 @@ func RoleFromName(name string) *Role {
|
||||
return NewManagerRole()
|
||||
case RoleSecureViewer:
|
||||
return NewSecureViewerRole()
|
||||
case RoleCoowner:
|
||||
return NewCoownerRole()
|
||||
default:
|
||||
return NewUnknownRole()
|
||||
}
|
||||
|
||||
26
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/assimilation.go
generated
vendored
26
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/assimilation.go
generated
vendored
@@ -178,7 +178,7 @@ func (t *Tree) workScanQueue() {
|
||||
}
|
||||
|
||||
if item.Recurse {
|
||||
err = t.WarmupIDCache(item.Path, true, false)
|
||||
err = t.WarmupIDCache(item.Path, true, true)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("path", item.Path).Msg("failed to warmup id cache")
|
||||
}
|
||||
@@ -435,11 +435,18 @@ func (t *Tree) assimilate(item scanItem) error {
|
||||
|
||||
// compare metadata mtime with actual mtime. if it matches AND the path hasn't changed (move operation)
|
||||
// we can skip the assimilation because the file was handled by us
|
||||
fi, err := os.Stat(item.Path)
|
||||
if err == nil && previousPath == item.Path {
|
||||
if mtime.Equal(fi.ModTime()) {
|
||||
return nil
|
||||
}
|
||||
fi, err := os.Lstat(item.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if previousPath == item.Path && mtime.Equal(fi.ModTime()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !fi.IsDir() && !fi.Mode().IsRegular() {
|
||||
t.log.Trace().Str("path", item.Path).Msg("skipping non-regular file")
|
||||
return nil
|
||||
}
|
||||
|
||||
// was it moved or copied/restored with a clashing id?
|
||||
@@ -783,12 +790,17 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
|
||||
isTrash(path) ||
|
||||
t.isUpload(path) ||
|
||||
t.isIndex(path) {
|
||||
return filepath.SkipDir
|
||||
return nil
|
||||
}
|
||||
if t.isRootPath(path) {
|
||||
return nil // ignore the root paths
|
||||
}
|
||||
|
||||
if !info.IsDir() && !info.Mode().IsRegular() {
|
||||
t.log.Trace().Str("path", path).Msg("skipping non-regular file")
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculate tree sizes
|
||||
if !info.IsDir() {
|
||||
dir := path
|
||||
|
||||
49
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/tree.go
generated
vendored
49
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/tree.go
generated
vendored
@@ -31,6 +31,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pkg/xattr"
|
||||
"github.com/rs/zerolog"
|
||||
"go-micro.dev/v4/store"
|
||||
"go.opentelemetry.io/otel"
|
||||
@@ -117,6 +118,9 @@ func New(lu node.PathLookup, bs node.Blobstore, um usermapper.Mapper, trashbin *
|
||||
personalSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.PersonalSpacePathTemplate))),
|
||||
projectSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.GeneralSpacePathTemplate))),
|
||||
}
|
||||
if err := t.checkStorage(); err != nil {
|
||||
return nil, errors.Wrap(err, "tree: unfit storage '"+o.Root+"'")
|
||||
}
|
||||
|
||||
// Start watching for fs events and put them into the queue
|
||||
if o.WatchFS {
|
||||
@@ -161,6 +165,51 @@ func New(lu node.PathLookup, bs node.Blobstore, um usermapper.Mapper, trashbin *
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *Tree) checkStorage() error {
|
||||
// check if the root path is a directory
|
||||
err := os.MkdirAll(t.options.Root, 0700)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create root path")
|
||||
}
|
||||
fi, err := os.Stat(t.options.Root)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "root path does not exist")
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return errors.New("root path is not a directory")
|
||||
}
|
||||
|
||||
// check if extended attributes are supported
|
||||
f, err := os.CreateTemp(t.options.Root, "posixfs-xattr-check-")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create file in root path")
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not close temp file")
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Remove(f.Name()); err != nil {
|
||||
t.log.Error().Err(err).Str("path", f.Name()).Msg("could not remove temp file")
|
||||
}
|
||||
}()
|
||||
|
||||
attrKey := "user.posixfs.test"
|
||||
attrVal := []byte("test")
|
||||
if err := xattr.Set(f.Name(), attrKey, attrVal); err != nil {
|
||||
return errors.Wrap(err, "extended attributes not supported")
|
||||
}
|
||||
|
||||
val, err := xattr.Get(f.Name(), attrKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "extended attributes not supported")
|
||||
}
|
||||
if string(val) != string(attrVal) {
|
||||
return errors.New("extended attribute mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tree) PublishEvent(ev interface{}) {
|
||||
if t.es == nil {
|
||||
return
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user