Compare commits

..

2 Commits

Author SHA1 Message Date
Viktor Scharf
da3a893634 getting all spaces using beta graph endpoint 2025-04-30 12:25:55 +02:00
Viktor Scharf
7bb95d057a check that created user has only one personal space 2025-04-29 16:26:39 +02:00
198 changed files with 2606 additions and 21039 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

43
go.mod
View File

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

92
go.sum
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1069,7 +1069,7 @@ class GraphHelper {
array $body = [],
array $headers = []
): ResponseInterface {
$url = self::getFullUrl($baseUrl, 'drives/' . $urlArguments);
$url = self::getBetaFullUrl($baseUrl, 'drives/' . $urlArguments);
return HttpRequestHelper::get($url, $xRequestId, $user, $password, $headers, $body);
}

View File

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

View File

@@ -1985,8 +1985,29 @@ trait Provisioning {
if ($this->isTestingWithLdap()) {
$this->deleteLdapUsersAndGroups();
}
$assertionFailed = false;
$errorMessage = '';
// check that created users have only one personal space
try {
$this->setResponse(
$this->spacesContext->listAllAvailableSpaces("admin", "%24filter=driveType+eq+personal")
);
$this->spacesContext->jsonRespondedShouldContainOnlyOneSpace();
$this->spacesContext->jsonRespondedShouldNotContainSpaceWithoutName();
} catch (\Throwable $e) {
$assertionFailed = true;
$errorMessage = $e->getMessage();
echo "\n[WARNING] Space assertion failed: " . $errorMessage . "\n";
}
$this->cleanupDatabaseUsers();
$this->cleanupDatabaseGroups();
if ($assertionFailed) {
throw new \Exception("Space assertion failed:\n" . $errorMessage);
}
}
/**

View File

@@ -1087,6 +1087,74 @@ class SpacesContext implements Context {
);
}
/**
* @return void
* @throws Exception
*/
public function jsonRespondedShouldContainOnlyOneSpace(): void {
$response = $response ?? $this->featureContext->getResponse();
$decodedResponse = $this->featureContext->getJsonDecodedResponse($response);
$userAdmin = $this->featureContext->getAdminUsername();
$aliases = [];
foreach ($decodedResponse['value'] as $space) {
$alias = $space['driveAlias'];
if (isset($aliases[$alias])) {
GraphHelper::disableSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
GraphHelper::deleteSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
Assert::fail(
"Duplicate space found: '$alias'\nResponse:\n" . json_encode($decodedResponse, JSON_PRETTY_PRINT)
);
}
$aliases[$alias] = true;
}
}
/**
* @return void
* @throws Exception
*/
public function jsonRespondedShouldNotContainSpaceWithoutName(): void {
$response = $response ?? $this->featureContext->getResponse();
$decodedResponse = $this->featureContext->getJsonDecodedResponse($response);
$userAdmin = $this->featureContext->getAdminUsername();
foreach ($decodedResponse['value'] as $space) {
if ($space['name'] === "") {
GraphHelper::disableSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
GraphHelper::deleteSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
Assert::fail(
"Space without name found. \nResponse:\n" . json_encode($decodedResponse, JSON_PRETTY_PRINT)
);
}
}
}
/**
* @Then /^the user "([^"]*)" should (not |)have a space called "([^"]*)"$/
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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