Repository: owncloud/ocis
Pull request #9782 (2024-08-12 11:06:03)

[docs-only] Fixes to the ocis full deployment example
Fixes: #9632 (ocis_full deployment example is missing to startup (at least) the notification service)

* This PR moves the start of the notification service from `inbucket.yml` to `ocis.yml`
* Improve `.env` readability + some fixes
* Make comments in yml files consistently be in a new line where applicapable

Note that #9772 ([docs-only] Add variables to .env to configure the data- and config directory) adds some good stuff with regards to volumes. I will rebase that PR post merging this one and check the texts and approve/merge it afterwards.


Patch:
diff --git a/deployments/examples/ocis_full/.env b/deployments/examples/ocis_full/.env
index ec835d18c5a..a2cac6b7b6d 100644
--- a/deployments/examples/ocis_full/.env
+++ b/deployments/examples/ocis_full/.env
@@ -1,12 +1,15 @@
+## Basic Settings ##
 # Define the docker compose log driver used.
 # Defaults to local
 LOG_DRIVER=
-# If you're on an internet facing server. comment out following line.
+# If you're on an internet facing server, comment out following line.
 # It skips certificate validation for various parts of Infinite Scale and is
 # needed when self signed certificates are used.
 INSECURE=true
 
-### Traefik Settings ###
+
+## Traefik Settings ##
+# Note: Traefik is always enabled and can't be disabled.
 # Serve Traefik dashboard.
 # Defaults to "false".
 TRAEFIK_DASHBOARD=
@@ -27,9 +30,10 @@ TRAEFIK_ACME_MAIL=
 TRAEFIK_ACME_CASERVER=
 
 
-### Infinite Scale Settings ###
+## Infinite Scale Settings ##
 # Beside Traefik, this service must stay enabled.
 # Disable only for testing purposes.
+# Note: the leading colon is required to enable the service.
 OCIS=:ocis.yml
 # The oCIS container image.
 # Defaults to "owncloud/ocis" which contains the production releases.
@@ -46,23 +50,22 @@ ADMIN_PASSWORD=
 # because their passwords are public. Defaults to "false".
 # Also see: https://doc.owncloud.com/ocis/latest/deployment/general/general-info.html#demo-users-and-groups
 DEMO_USERS=
-# Define the loglevel used.
+# Define the oCIS loglevel used.
 # For more details see:
 # https://doc.owncloud.com/ocis/latest/deployment/services/env-vars-special-scope.html
 LOG_LEVEL=
 # Define the kind of logging.
 # The default log can be read by machines.
-# Set this to true to make the log human readable
+# Set this to true to make the log human readable.
 # LOG_PRETTY=true
 
-# S3 Storage configuration
-#
-# - optional
-#
+# S3 Storage configuration - optional
 # Infinite Scale supports S3 storage as primary storage.
-# Per default, S3 storage is disabled and we use the local filesystem.
-# To enable S3 storage, uncomment the following lines and configure the S3 storage.
-# The leading colon is required to enable the service.
+# Per default, S3 storage is disabled and the local filesystem is used.
+# To enable S3 storage, uncomment the following line and configure the S3 storage.
+# For more details see:
+# https://doc.owncloud.com/ocis/next/deployment/storage/s3.html
+# Note: the leading colon is required to enable the service.
 #S3NG=:s3ng.yml
 # Configure the S3 storage endpoint. Defaults to "http://minio:9000" for testing purposes.
 S3NG_ENDPOINT=
@@ -74,8 +77,8 @@ S3NG_ACCESS_KEY=
 S3NG_SECRET_KEY=
 # S3 bucket. Defaults to "ocis"
 S3NG_BUCKET=
-# Add local minio S3 storage to the docker-compose file.
-# This is needed for testing purposes.
+#
+# For testing purposes, add local minio S3 storage to the docker-compose file.
 # The leading colon is required to enable the service.
 #S3NG_MINIO=:minio.yml
 # Minio domain. Defaults to "minio.owncloud.test".
@@ -84,7 +87,7 @@ MINIO_DOMAIN=
 # Define SMPT settings if you would like to send Infinite Scale email notifications.
 # For more details see:
 # https://doc.owncloud.com/ocis/latest/deployment/services/s-list/notifications.html
-# NOTE: this doesn't work if you are using inbucket.
+# NOTE: when configuring Inbucket, these settings have no effect, see inbucket.yml for details.
 # SMTP host to connect to.
 SMTP_HOST=
 # Port of the SMTP host to connect to.
@@ -101,19 +104,21 @@ SMTP_AUTHENTICATION=
 # Allow insecure connections to the SMTP server. Defaults to false.
 SMTP_INSECURE=
 
+
 ## Default Enabled Services ##
 
 ### Apache Tika Content Analysis Toolkit ###
 # Tika (search) is enabled by default, comment if not required.
-# The leading colon is required to enable the service.
+# Note: the leading colon is required to enable the service.
 TIKA=:tika.yml
 # Set the desired docker image tag or digest.
 # Defaults to "latest"
 TIKA_IMAGE=
 
+
 ### Collabora Settings ###
 # Collabora web office is default enabled, comment if not required.
-# The leading colon is required to enable the service.
+# Note: the leading colon is required to enable the service.
 COLLABORA=:collabora.yml
 # Domain of Collabora, where you can find the frontend.
 # Defaults to "collabora.owncloud.test"
@@ -135,18 +140,21 @@ COLLABORA_SSL_ENABLE=false
 # Please comment out the following line:
 COLLABORA_SSL_VERIFICATION=false
 
-### Supplemental Configurations ###
+
+## Supplemental Configurations ##
 # If you want to use supplemental configurations,
 # you need to uncomment lines containing :path/file.yml
-# and configure the service if required.
+# and configure the service as required.
 
-## Debugging - Monitoring ##
+
+### Debugging - Monitoring ###
 # Please see documentation at: https://owncloud.dev/ocis/deployment/monitoring-tracing/
-# The leading colon is required to enable the service.
+# Note: the leading colon is required to enable the service.
 #MONITORING=:monitoring_tracing/monitoring.yml
 
-## Uppy Companion Settings ##
-# The leading colon is required to enable the service.
+
+### Uppy Companion Settings ###
+# Note: the leading colon is required to enable the service.
 #CLOUD_IMPORTER=:cloudimporter.yml
 ## The docker image to be used for uppy companion.
 # owncloud has built a container with public link import support.
@@ -158,31 +166,35 @@ COMPANION_DOMAIN=
 COMPANION_ONEDRIVE_KEY=
 COMPANION_ONEDRIVE_SECRET=
 
-## Virusscanner Settings ##
-# The leading colon is required to enable the service.
+
+### Virusscanner Settings ###
+# Note: the leading colon is required to enable the service.
 # CLAMAV=:clamav.yml
 # Image version of the ClamAV container.
 # Defaults to "latest"
 CLAMAV_DOCKER_TAG=
 
-## OnlyOffice Settings ##
-# The leading colon is required to enable the service.
+
+### OnlyOffice Settings ###
+# Note: the leading colon is required to enable the service.
 #ONLYOFFICE=:onlyoffice.yml
 # Domain for OnlyOffice. Defaults to "onlyoffice.owncloud.test".
 ONLYOFFICE_DOMAIN=
 # Domain for the wopiserver which handles OnlyOffice.
 WOPISERVER_ONLYOFFICE_DOMAIN=
 
-## Inbucket Settings ##
+
+### Inbucket Settings ###
 # Inbucket is a mail catcher tool for testing purposes.
 # DO NOT use in Production.
-# The leading colon is required to enable the service.
+# Note: the leading colon is required to enable the service.
 #INBUCKET=:inbucket.yml
 # email server (in this case inbucket acts as mail catcher).
 # Domain for Inbucket. Defaults to "mail.owncloud.test".
 INBUCKET_DOMAIN=
 
-### IMPORTANT ###
+
+## 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
diff --git a/deployments/examples/ocis_full/docker-compose.yml b/deployments/examples/ocis_full/docker-compose.yml
index 15a2389fe8b..0c34f646042 100644
--- a/deployments/examples/ocis_full/docker-compose.yml
+++ b/deployments/examples/ocis_full/docker-compose.yml
@@ -34,7 +34,8 @@ services:
       - "certs:/certs"
     labels:
       - "traefik.enable=${TRAEFIK_DASHBOARD:-false}"
-      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_BASIC_AUTH_USERS:-admin:$$apr1$$4vqie50r$$YQAmQdtmz5n9rEALhxJ4l.}" # defaults to admin:admin
+       # defaults to admin:admin
+      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_BASIC_AUTH_USERS:-admin:$$apr1$$4vqie50r$$YQAmQdtmz5n9rEALhxJ4l.}"
       - "traefik.http.routers.traefik.entrypoints=https"
       - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN:-traefik.owncloud.test}`)"
       - "traefik.http.routers.traefik.middlewares=traefik-auth"
diff --git a/deployments/examples/ocis_full/inbucket.yml b/deployments/examples/ocis_full/inbucket.yml
index 495006edf2e..9ec595a09fe 100644
--- a/deployments/examples/ocis_full/inbucket.yml
+++ b/deployments/examples/ocis_full/inbucket.yml
@@ -2,13 +2,12 @@
 services:
   ocis:
     environment:
-      # enable the notifications service
-      OCIS_ADD_RUN_SERVICES": "notifications"
       NOTIFICATIONS_SMTP_HOST: inbucket
       NOTIFICATIONS_SMTP_PORT: 2500
       NOTIFICATIONS_SMTP_SENDER: oCIS notifications <notifications@${OCIS_DOMAIN:-ocis.owncloud.test}>
       NOTIFICATIONS_SMTP_USERNAME: notifications@${OCIS_DOMAIN:-ocis.owncloud.test}
-      NOTIFICATIONS_SMTP_INSECURE: "true" # the mail catcher uses self signed certificates
+      # the mail catcher uses self signed certificates
+      NOTIFICATIONS_SMTP_INSECURE: "true"
 
   inbucket:
     image: inbucket/inbucket
diff --git a/deployments/examples/ocis_full/ocis.yml b/deployments/examples/ocis_full/ocis.yml
index 674adf01c66..1d4ad9f0874 100644
--- a/deployments/examples/ocis_full/ocis.yml
+++ b/deployments/examples/ocis_full/ocis.yml
@@ -16,12 +16,16 @@ services:
     # therefore we ignore the error and then start the ocis server
     command: ["-c", "ocis init || true; ocis server"]
     environment:
+      # enable the notifications service as it is not started automatically
+      OCIS_ADD_RUN_SERVICES": "notifications"
       OCIS_URL: https://${OCIS_DOMAIN:-ocis.owncloud.test}
       OCIS_LOG_LEVEL: ${LOG_LEVEL:-info}
       OCIS_LOG_COLOR: "${LOG_PRETTY:-false}"
       OCIS_LOG_PRETTY: "${LOG_PRETTY:-false}"
-      PROXY_TLS: "false" # do not use SSL between Traefik and oCIS
-      GATEWAY_GRPC_ADDR: 0.0.0.0:9142 # make the REVA gateway accessible to the app drivers
+      # do not use SSL between Traefik and oCIS
+      PROXY_TLS: "false"
+      # make the REVA gateway accessible to the app drivers
+      GATEWAY_GRPC_ADDR: 0.0.0.0:9142
       # INSECURE: needed if oCIS / Traefik is using self generated certificates
       OCIS_INSECURE: "${INSECURE:-false}"
       # basic auth (not recommended, but needed for eg. WebDav clients that do not support OpenID Connect)
diff --git a/deployments/examples/ocis_full/s3ng.yml b/deployments/examples/ocis_full/s3ng.yml
index 914ba9e4535..cde233e3392 100644
--- a/deployments/examples/ocis_full/s3ng.yml
+++ b/deployments/examples/ocis_full/s3ng.yml
@@ -4,7 +4,8 @@ services:
     environment:
       # activate s3ng storage driver
       STORAGE_USERS_DRIVER: s3ng
-      STORAGE_SYSTEM_DRIVER: ocis # keep system data on ocis storage since this are only small files atm
+      # keep system data on ocis storage since this are only small files atm
+      STORAGE_SYSTEM_DRIVER: ocis
       # s3ng specific settings
       STORAGE_USERS_S3NG_ENDPOINT: ${S3NG_ENDPOINT:-http://minio:9000}
       STORAGE_USERS_S3NG_REGION: ${S3NG_REGION:-default}
diff --git a/docs/helpers/env_vars.yaml b/docs/helpers/env_vars.yaml
index a198084e625..5131e64aca7 100644
--- a/docs/helpers/env_vars.yaml
+++ b/docs/helpers/env_vars.yaml
@@ -1203,50 +1203,6 @@ AUDIT_TRACING_TYPE:
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
-AUTH_APP_CORS_ALLOW_CREDENTIALS:
-  name: OCIS_CORS_ALLOW_CREDENTIALS;AUTH_APP_CORS_ALLOW_CREDENTIALS
-  defaultValue: "true"
-  type: bool
-  description: 'Allow credentials for CORS.See following chapter for more details:
-    *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials.'
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
-AUTH_APP_CORS_ALLOW_HEADERS:
-  name: OCIS_CORS_ALLOW_HEADERS;AUTH_APP_CORS_ALLOW_HEADERS
-  defaultValue: '[Authorization Origin Content-Type Accept X-Requested-With X-Request-Id
-    Ocs-Apirequest]'
-  type: '[]string'
-  description: 'A list of allowed CORS headers. See following chapter for more details:
-    *Access-Control-Request-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers.
-    See the Environment Variable Types description for more details.'
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
-AUTH_APP_CORS_ALLOW_METHODS:
-  name: OCIS_CORS_ALLOW_METHODS;AUTH_APP_CORS_ALLOW_METHODS
-  defaultValue: '[GET POST DELETE]'
-  type: '[]string'
-  description: 'A list of allowed CORS methods. See following chapter for more details:
-    *Access-Control-Request-Method* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Method.
-    See the Environment Variable Types description for more details.'
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
-AUTH_APP_CORS_ALLOW_ORIGINS:
-  name: OCIS_CORS_ALLOW_ORIGINS;AUTH_APP_CORS_ALLOW_ORIGINS
-  defaultValue: '[*]'
-  type: '[]string'
-  description: 'A list of allowed CORS origins. See following chapter for more details:
-    *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin.
-    See the Environment Variable Types description for more details.'
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
 AUTH_APP_DEBUG_ADDR:
   name: AUTH_APP_DEBUG_ADDR
   defaultValue: 127.0.0.1:9245
@@ -1285,16 +1241,6 @@ AUTH_APP_DEBUG_ZPAGES:
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
-AUTH_APP_ENABLE_IMPERSONATION:
-  name: AUTH_APP_ENABLE_IMPERSONATION
-  defaultValue: "false"
-  type: bool
-  description: Allows admins to create app tokens for other users. Used for migration.
-    Do NOT use in productive deployments.
-  introductionVersion: '%%NEXT%%'
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
 AUTH_APP_GRPC_ADDR:
   name: AUTH_APP_GRPC_ADDR
   defaultValue: 127.0.0.1:9246
@@ -1313,24 +1259,6 @@ AUTH_APP_GRPC_PROTOCOL:
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
-AUTH_APP_HTTP_ADDR:
-  name: AUTH_APP_HTTP_ADDR
-  defaultValue: 127.0.0.1:9247
-  type: string
-  description: The bind address of the HTTP service.
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
-AUTH_APP_HTTP_ROOT:
-  name: AUTH_APP_HTTP_ROOT
-  defaultValue: /
-  type: string
-  description: Subdirectory that serves as the root for this HTTP service.
-  introductionVersion: pre5.0
-  deprecationVersion: ""
-  removalVersion: ""
-  deprecationInfo: ""
 AUTH_APP_JWT_SECRET:
   name: OCIS_JWT_SECRET;AUTH_APP_JWT_SECRET
   defaultValue: ""
@@ -7681,12 +7609,12 @@ OCDAV_WEBDAV_NAMESPACE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_ADMIN_USER_ID:
-  name: OCIS_ADMIN_USER_ID;IDM_ADMIN_USER_ID
+  name: OCIS_ADMIN_USER_ID;STORAGE_USERS_PURGE_TRASH_BIN_USER_ID
   defaultValue: ""
   type: string
-  description: ID of the user that should receive admin privileges. Consider that
-    the UUID can be encoded in some LDAP deployment configurations like in .ldif files.
-    These need to be decoded beforehand.
+  description: ID of the user who collects all necessary information for deletion.
+    Consider that the UUID can be encoded in some LDAP deployment configurations like
+    in .ldif files. These need to be decoded beforehand.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
@@ -7702,7 +7630,7 @@ OCIS_ASSET_THEMES_PATH:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_ASYNC_UPLOADS:
-  name: OCIS_ASYNC_UPLOADS;SEARCH_EVENTS_ASYNC_UPLOADS
+  name: OCIS_ASYNC_UPLOADS
   defaultValue: "true"
   type: bool
   description: Enable asynchronous file uploads.
@@ -7711,28 +7639,28 @@ OCIS_ASYNC_UPLOADS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_AUTH_PASSWORD:
-  name: OCIS_CACHE_AUTH_PASSWORD;FRONTEND_OCS_STAT_CACHE_AUTH_PASSWORD
+  name: OCIS_CACHE_AUTH_PASSWORD;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE_AUTH_PASSWORD
   defaultValue: ""
   type: string
-  description: The password to use for authentication. Only applies when using the
-    'nats-js-kv' store type.
+  description: The password to authenticate with the store. Only applies when store
+    type 'nats-js-kv' is configured.
   introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_AUTH_USERNAME:
-  name: OCIS_CACHE_AUTH_USERNAME;FRONTEND_OCS_STAT_CACHE_AUTH_USERNAME
+  name: OCIS_CACHE_AUTH_USERNAME;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE_AUTH_USERNAME
   defaultValue: ""
   type: string
-  description: The username to use for authentication. Only applies when using the
-    'nats-js-kv' store type.
+  description: The username to authenticate with the store. Only applies when store
+    type 'nats-js-kv' is configured.
   introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_DATABASE:
   name: OCIS_CACHE_DATABASE
-  defaultValue: cache-stat
+  defaultValue: cache-userinfo
   type: string
   description: The database name the configured store should use.
   introductionVersion: pre5.0
@@ -7740,54 +7668,55 @@ OCIS_CACHE_DATABASE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_DISABLE_PERSISTENCE:
-  name: OCIS_CACHE_DISABLE_PERSISTENCE;FRONTEND_OCS_STAT_CACHE_DISABLE_PERSISTENCE
-  defaultValue: "false"
+  name: OCIS_CACHE_DISABLE_PERSISTENCE;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE_DISABLE_PERSISTENCE
+  defaultValue: "true"
   type: bool
-  description: Disable persistence of the cache. Only applies when using the 'nats-js-kv'
-    store type. Defaults to false.
+  description: Disables persistence of the store. Only applies when store type 'nats-js-kv'
+    is configured. Defaults to true.
   introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_SIZE:
-  name: OCIS_CACHE_SIZE;FRONTEND_OCS_STAT_CACHE_SIZE
+  name: OCIS_CACHE_SIZE;PROXY_OIDC_USERINFO_CACHE_SIZE
   defaultValue: "0"
   type: int
-  description: Max number of entries to hold in the cache.
+  description: The maximum quantity of items in the user info cache. Only applies
+    when store type 'ocmem' is configured. Defaults to 512 which is derived from the
+    ocmem package though not explicitly set as default.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_STORE:
-  name: OCIS_CACHE_STORE;FRONTEND_OCS_STAT_CACHE_STORE
-  defaultValue: memory
+  name: OCIS_CACHE_STORE;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE
+  defaultValue: nats-js-kv
   type: string
-  description: 'The type of the cache store. Supported values are: ''memory'', ''redis-sentinel'',
-    ''nats-js-kv'', ''noop''. See the text description for details.'
-  introductionVersion: pre5.0
+  description: 'The type of the signing key store. Supported values are: ''redis-sentinel'',
+    ''nats-js-kv'' and ''ocisstoreservice'' (deprecated). See the text description
+    for details.'
+  introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_STORE_NODES:
-  name: OCIS_CACHE_STORE_NODES;FRONTEND_OCS_STAT_CACHE_STORE_NODES
+  name: OCIS_CACHE_STORE_NODES;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE_NODES
   defaultValue: '[127.0.0.1:9233]'
   type: '[]string'
-  description: A list of nodes to access the configured store. This has no effect
-    when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes
-    are used is dependent on the library of the configured store. See the Environment
-    Variable Types description for more details.
-  introductionVersion: pre5.0
+  description: A list of nodes to access the configured store. Note that the behaviour
+    how nodes are used is dependent on the library of the configured store. See the
+    Environment Variable Types description for more details.
+  introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_CACHE_TTL:
-  name: OCIS_CACHE_TTL;FRONTEND_OCS_STAT_CACHE_TTL
-  defaultValue: 5m0s
+  name: OCIS_CACHE_TTL;PROXY_PRESIGNEDURL_SIGNING_KEYS_STORE_TTL
+  defaultValue: 12h0m0s
   type: Duration
-  description: Default time to live for user info in the cache. Only applied when
-    access tokens has no expiration. See the Environment Variable Types description
-    for more details.
-  introductionVersion: pre5.0
+  description: Default time to live for signing keys. See the Environment Variable
+    Types description for more details.
+  introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
@@ -7860,7 +7789,7 @@ OCIS_CORS_MAX_AGE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_DECOMPOSEDFS_METADATA_BACKEND:
-  name: OCIS_DECOMPOSEDFS_METADATA_BACKEND;STORAGE_SYSTEM_OCIS_METADATA_BACKEND
+  name: OCIS_DECOMPOSEDFS_METADATA_BACKEND;STORAGE_USERS_OCIS_METADATA_BACKEND
   defaultValue: messagepack
   type: string
   description: The backend to use for storing metadata. Supported values are 'messagepack'
@@ -7932,7 +7861,7 @@ OCIS_DISABLE_VERSIONING:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EDITION:
-  name: OCIS_EDITION;FRONTEND_EDITION
+  name: OCIS_EDITION;OCDAV_EDITION
   defaultValue: Community
   type: string
   description: Edition of oCIS. Used for branding purposes.
@@ -7960,63 +7889,63 @@ OCIS_ENABLE_RESHARING:
   removalVersion: ""
   deprecationInfo: Resharing will be removed in the future.
 OCIS_EVENTS_AUTH_PASSWORD:
-  name: OCIS_EVENTS_AUTH_PASSWORD;SEARCH_EVENTS_AUTH_PASSWORD
+  name: OCIS_EVENTS_AUTH_PASSWORD;PROXY_EVENTS_AUTH_PASSWORD
   defaultValue: ""
   type: string
   description: The password to authenticate with the events broker. The events broker
     is the ocis service which receives and delivers events between the services.
-  introductionVersion: "5.0"
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EVENTS_AUTH_USERNAME:
-  name: OCIS_EVENTS_AUTH_USERNAME;SEARCH_EVENTS_AUTH_USERNAME
+  name: OCIS_EVENTS_AUTH_USERNAME;PROXY_EVENTS_AUTH_USERNAME
   defaultValue: ""
   type: string
   description: The username to authenticate with the events broker. The events broker
     is the ocis service which receives and delivers events between the services.
-  introductionVersion: "5.0"
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EVENTS_CLUSTER:
-  name: OCIS_EVENTS_CLUSTER;SEARCH_EVENTS_CLUSTER
+  name: OCIS_EVENTS_CLUSTER;PROXY_EVENTS_CLUSTER
   defaultValue: ocis-cluster
   type: string
   description: The clusterID of the event system. The event system is the message
     queuing service. It is used as message broker for the microservice architecture.
-    Mandatory when using NATS as event system.
-  introductionVersion: pre5.0
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EVENTS_ENABLE_TLS:
-  name: OCIS_EVENTS_ENABLE_TLS;SEARCH_EVENTS_ENABLE_TLS
+  name: OCIS_EVENTS_ENABLE_TLS;PROXY_EVENTS_ENABLE_TLS
   defaultValue: "false"
   type: bool
   description: Enable TLS for the connection to the events broker. The events broker
     is the ocis service which receives and delivers events between the services.
-  introductionVersion: pre5.0
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EVENTS_ENDPOINT:
-  name: OCIS_EVENTS_ENDPOINT;SEARCH_EVENTS_ENDPOINT
+  name: OCIS_EVENTS_ENDPOINT;PROXY_EVENTS_ENDPOINT
   defaultValue: 127.0.0.1:9233
   type: string
   description: The address of the event system. The event system is the message queuing
-    service. It is used as message broker for the microservice architecture.
-  introductionVersion: pre5.0
+    service. It is used as message broker for the microservice architecture. Set to
+    a empty string to disable emitting events.
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE:
-  name: OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SEARCH_EVENTS_TLS_ROOT_CA_CERTIFICATE
+  name: OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;PROXY_EVENTS_TLS_ROOT_CA_CERTIFICATE
   defaultValue: ""
   type: string
   description: The root CA certificate used to validate the server's TLS certificate.
-    If provided SEARCH_EVENTS_TLS_INSECURE will be seen as false.
-  introductionVersion: pre5.0
+    If provided PROXY_EVENTS_TLS_INSECURE will be seen as false.
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
@@ -8084,16 +8013,16 @@ OCIS_HTTP_TLS_KEY:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_INSECURE:
-  name: OCIS_INSECURE;SEARCH_EXTRACTOR_CS3SOURCE_INSECURE
+  name: OCIS_INSECURE;PROXY_EVENTS_TLS_INSECURE
   defaultValue: "false"
   type: bool
-  description: Ignore untrusted SSL certificates when connecting to the CS3 source.
-  introductionVersion: pre5.0
+  description: Whether to verify the server TLS certificates.
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_JWT_SECRET:
-  name: OCIS_JWT_SECRET;STORAGE_SHARES_JWT_SECRET
+  name: OCIS_JWT_SECRET;GATEWAY_JWT_SECRET
   defaultValue: ""
   type: string
   description: The secret to mint and validate jwt tokens.
@@ -8102,7 +8031,7 @@ OCIS_JWT_SECRET:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_BASE_PATH:
-  name: OCIS_KEYCLOAK_BASE_PATH;INVITATIONS_KEYCLOAK_BASE_PATH
+  name: OCIS_KEYCLOAK_BASE_PATH;GRAPH_KEYCLOAK_BASE_PATH
   defaultValue: ""
   type: string
   description: The URL to access keycloak.
@@ -8111,16 +8040,16 @@ OCIS_KEYCLOAK_BASE_PATH:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_CLIENT_ID:
-  name: OCIS_KEYCLOAK_CLIENT_ID;INVITATIONS_KEYCLOAK_CLIENT_ID
+  name: OCIS_KEYCLOAK_CLIENT_ID;GRAPH_KEYCLOAK_CLIENT_ID
   defaultValue: ""
   type: string
-  description: The client ID to authenticate with keycloak.
+  description: The client id to authenticate with keycloak.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_CLIENT_REALM:
-  name: OCIS_KEYCLOAK_CLIENT_REALM;INVITATIONS_KEYCLOAK_CLIENT_REALM
+  name: OCIS_KEYCLOAK_CLIENT_REALM;GRAPH_KEYCLOAK_CLIENT_REALM
   defaultValue: ""
   type: string
   description: The realm the client is defined in.
@@ -8129,7 +8058,7 @@ OCIS_KEYCLOAK_CLIENT_REALM:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_CLIENT_SECRET:
-  name: OCIS_KEYCLOAK_CLIENT_SECRET;INVITATIONS_KEYCLOAK_CLIENT_SECRET
+  name: OCIS_KEYCLOAK_CLIENT_SECRET;GRAPH_KEYCLOAK_CLIENT_SECRET
   defaultValue: ""
   type: string
   description: The client secret to use in authentication.
@@ -8138,7 +8067,7 @@ OCIS_KEYCLOAK_CLIENT_SECRET:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_INSECURE_SKIP_VERIFY:
-  name: OCIS_KEYCLOAK_INSECURE_SKIP_VERIFY;INVITATIONS_KEYCLOAK_INSECURE_SKIP_VERIFY
+  name: OCIS_KEYCLOAK_INSECURE_SKIP_VERIFY;GRAPH_KEYCLOAK_INSECURE_SKIP_VERIFY
   defaultValue: "false"
   type: bool
   description: Disable TLS certificate validation for Keycloak connections. Do not
@@ -8148,7 +8077,7 @@ OCIS_KEYCLOAK_INSECURE_SKIP_VERIFY:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_KEYCLOAK_USER_REALM:
-  name: OCIS_KEYCLOAK_USER_REALM;INVITATIONS_KEYCLOAK_USER_REALM
+  name: OCIS_KEYCLOAK_USER_REALM;GRAPH_KEYCLOAK_USER_REALM
   defaultValue: ""
   type: string
   description: The realm users are defined.
@@ -8157,8 +8086,8 @@ OCIS_KEYCLOAK_USER_REALM:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_BIND_DN:
-  name: OCIS_LDAP_BIND_DN;GROUPS_LDAP_BIND_DN
-  defaultValue: uid=reva,ou=sysusers,o=libregraph-idm
+  name: OCIS_LDAP_BIND_DN;IDP_LDAP_BIND_DN
+  defaultValue: uid=idp,ou=sysusers,o=libregraph-idm
   type: string
   description: LDAP DN to use for simple bind authentication with the target LDAP
     server.
@@ -8167,7 +8096,7 @@ OCIS_LDAP_BIND_DN:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_BIND_PASSWORD:
-  name: OCIS_LDAP_BIND_PASSWORD;GROUPS_LDAP_BIND_PASSWORD
+  name: OCIS_LDAP_BIND_PASSWORD;IDP_LDAP_BIND_PASSWORD
   defaultValue: ""
   type: string
   description: Password to use for authenticating the 'bind_dn'.
@@ -8176,31 +8105,31 @@ OCIS_LDAP_BIND_PASSWORD:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_CACERT:
-  name: OCIS_LDAP_CACERT;GROUPS_LDAP_CACERT
+  name: OCIS_LDAP_CACERT;IDP_LDAP_TLS_CACERT
   defaultValue: /var/lib/ocis/idm/ldap.crt
   type: string
   description: Path/File name for the root CA certificate (in PEM format) used to
     validate TLS server certificates of the LDAP service. If not defined, the root
-    directory derives from $OCIS_BASE_DATA_PATH:/idm.
+    directory derives from $OCIS_BASE_DATA_PATH:/idp.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_DISABLE_USER_MECHANISM:
-  name: OCIS_LDAP_DISABLE_USER_MECHANISM;GRAPH_DISABLE_USER_MECHANISM
+  name: OCIS_LDAP_DISABLE_USER_MECHANISM;USERS_LDAP_DISABLE_USER_MECHANISM
   defaultValue: attribute
   type: string
-  description: An option to control the behavior for disabling users. Supported options
+  description: An option to control the behavior for disabling users. Valid options
     are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API
     will add the user to the configured group for disabled users, if set to 'attribute'
     this will be done in the ldap user entry, if set to 'none' the disable request
-    is not processed. Default is 'attribute'.
+    is not processed.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_DISABLED_USERS_GROUP_DN:
-  name: OCIS_LDAP_DISABLED_USERS_GROUP_DN;GRAPH_DISABLED_USERS_GROUP_DN
+  name: OCIS_LDAP_DISABLED_USERS_GROUP_DN;USERS_LDAP_DISABLED_USERS_GROUP_DN
   defaultValue: cn=DisabledUsersGroup,ou=groups,o=libregraph-idm
   type: string
   description: The distinguished name of the group to which added users will be classified
@@ -8210,7 +8139,7 @@ OCIS_LDAP_DISABLED_USERS_GROUP_DN:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_BASE_DN:
-  name: OCIS_LDAP_GROUP_BASE_DN;GROUPS_LDAP_GROUP_BASE_DN
+  name: OCIS_LDAP_GROUP_BASE_DN;USERS_LDAP_GROUP_BASE_DN
   defaultValue: ou=groups,o=libregraph-idm
   type: string
   description: Search base DN for looking up LDAP groups.
@@ -8219,7 +8148,7 @@ OCIS_LDAP_GROUP_BASE_DN:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_FILTER:
-  name: OCIS_LDAP_GROUP_FILTER;GROUPS_LDAP_GROUP_FILTER
+  name: OCIS_LDAP_GROUP_FILTER;USERS_LDAP_GROUP_FILTER
   defaultValue: ""
   type: string
   description: LDAP filter to add to the default filters for group searches.
@@ -8228,17 +8157,17 @@ OCIS_LDAP_GROUP_FILTER:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_OBJECTCLASS:
-  name: OCIS_LDAP_GROUP_OBJECTCLASS;GROUPS_LDAP_GROUP_OBJECTCLASS
+  name: OCIS_LDAP_GROUP_OBJECTCLASS;USERS_LDAP_GROUP_OBJECTCLASS
   defaultValue: groupOfNames
   type: string
   description: The object class to use for groups in the default group search filter
-    ('groupOfNames').
+    like 'groupOfNames'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_DISPLAYNAME:
-  name: OCIS_LDAP_GROUP_SCHEMA_DISPLAYNAME;GROUPS_LDAP_GROUP_SCHEMA_DISPLAYNAME
+  name: OCIS_LDAP_GROUP_SCHEMA_DISPLAYNAME;USERS_LDAP_GROUP_SCHEMA_DISPLAYNAME
   defaultValue: cn
   type: string
   description: LDAP Attribute to use for the displayname of groups (often the same
@@ -8248,7 +8177,7 @@ OCIS_LDAP_GROUP_SCHEMA_DISPLAYNAME:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_GROUPNAME:
-  name: OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;GROUPS_LDAP_GROUP_SCHEMA_GROUPNAME
+  name: OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;USERS_LDAP_GROUP_SCHEMA_GROUPNAME
   defaultValue: cn
   type: string
   description: LDAP Attribute to use for the name of groups.
@@ -8257,17 +8186,17 @@ OCIS_LDAP_GROUP_SCHEMA_GROUPNAME:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_ID:
-  name: OCIS_LDAP_GROUP_SCHEMA_ID;GROUPS_LDAP_GROUP_SCHEMA_ID
+  name: OCIS_LDAP_GROUP_SCHEMA_ID;USERS_LDAP_GROUP_SCHEMA_ID
   defaultValue: ownclouduuid
   type: string
-  description: LDAP Attribute to use as the unique id for groups. This should be a
+  description: LDAP Attribute to use as the unique ID for groups. This should be a
     stable globally unique ID like a UUID.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING:
-  name: OCIS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING;GROUPS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING
+  name: OCIS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING;USERS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING
   defaultValue: "false"
   type: bool
   description: Set this to true if the defined 'id' attribute for groups is of the
@@ -8278,7 +8207,7 @@ OCIS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_MAIL:
-  name: OCIS_LDAP_GROUP_SCHEMA_MAIL;GROUPS_LDAP_GROUP_SCHEMA_MAIL
+  name: OCIS_LDAP_GROUP_SCHEMA_MAIL;USERS_LDAP_GROUP_SCHEMA_MAIL
   defaultValue: mail
   type: string
   description: LDAP Attribute to use for the email address of groups (can be empty).
@@ -8287,7 +8216,7 @@ OCIS_LDAP_GROUP_SCHEMA_MAIL:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCHEMA_MEMBER:
-  name: OCIS_LDAP_GROUP_SCHEMA_MEMBER;GROUPS_LDAP_GROUP_SCHEMA_MEMBER
+  name: OCIS_LDAP_GROUP_SCHEMA_MEMBER;USERS_LDAP_GROUP_SCHEMA_MEMBER
   defaultValue: member
   type: string
   description: LDAP Attribute that is used for group members.
@@ -8296,17 +8225,17 @@ OCIS_LDAP_GROUP_SCHEMA_MEMBER:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_GROUP_SCOPE:
-  name: OCIS_LDAP_GROUP_SCOPE;GROUPS_LDAP_GROUP_SCOPE
+  name: OCIS_LDAP_GROUP_SCOPE;USERS_LDAP_GROUP_SCOPE
   defaultValue: sub
   type: string
-  description: LDAP search scope to use when looking up groups. Supported scopes are
+  description: LDAP search scope to use when looking up groups. Supported values are
     'base', 'one' and 'sub'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_INSECURE:
-  name: OCIS_LDAP_INSECURE;GROUPS_LDAP_INSECURE
+  name: OCIS_LDAP_INSECURE;IDP_INSECURE
   defaultValue: "false"
   type: bool
   description: Disable TLS certificate validation for the LDAP connections. Do not
@@ -8316,7 +8245,7 @@ OCIS_LDAP_INSECURE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_SERVER_WRITE_ENABLED:
-  name: OCIS_LDAP_SERVER_WRITE_ENABLED;FRONTEND_LDAP_SERVER_WRITE_ENABLED
+  name: OCIS_LDAP_SERVER_WRITE_ENABLED;GRAPH_LDAP_SERVER_WRITE_ENABLED
   defaultValue: "true"
   type: bool
   description: Allow creating, modifying and deleting LDAP users via the GRAPH API.
@@ -8328,17 +8257,16 @@ OCIS_LDAP_SERVER_WRITE_ENABLED:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_URI:
-  name: OCIS_LDAP_URI;GROUPS_LDAP_URI
+  name: OCIS_LDAP_URI;IDP_LDAP_URI
   defaultValue: ldaps://localhost:9235
   type: string
-  description: URI of the LDAP Server to connect to. Supported URI schemes are 'ldaps://'
-    and 'ldap://'
+  description: Url of the LDAP service to use as IDP.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_BASE_DN:
-  name: OCIS_LDAP_USER_BASE_DN;GROUPS_LDAP_USER_BASE_DN
+  name: OCIS_LDAP_USER_BASE_DN;IDP_LDAP_BASE_DN
   defaultValue: ou=users,o=libregraph-idm
   type: string
   description: Search base DN for looking up LDAP users.
@@ -8356,7 +8284,7 @@ OCIS_LDAP_USER_ENABLED_ATTRIBUTE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_FILTER:
-  name: OCIS_LDAP_USER_FILTER;GROUPS_LDAP_USER_FILTER
+  name: OCIS_LDAP_USER_FILTER;IDP_LDAP_FILTER
   defaultValue: ""
   type: string
   description: LDAP filter to add to the default filters for user search like '(objectclass=ownCloud)'.
@@ -8365,17 +8293,16 @@ OCIS_LDAP_USER_FILTER:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_OBJECTCLASS:
-  name: OCIS_LDAP_USER_OBJECTCLASS;GROUPS_LDAP_USER_OBJECTCLASS
+  name: OCIS_LDAP_USER_OBJECTCLASS;IDP_LDAP_OBJECTCLASS
   defaultValue: inetOrgPerson
   type: string
-  description: The object class to use for users in the default user search filter
-    ('inetOrgPerson').
+  description: LDAP User ObjectClass like 'inetOrgPerson'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_DISPLAYNAME:
-  name: OCIS_LDAP_USER_SCHEMA_DISPLAYNAME;GROUPS_LDAP_USER_SCHEMA_DISPLAYNAME
+  name: OCIS_LDAP_USER_SCHEMA_DISPLAYNAME;USERS_LDAP_USER_SCHEMA_DISPLAYNAME
   defaultValue: displayname
   type: string
   description: LDAP Attribute to use for the displayname of users.
@@ -8384,17 +8311,16 @@ OCIS_LDAP_USER_SCHEMA_DISPLAYNAME:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_ID:
-  name: OCIS_LDAP_USER_SCHEMA_ID;GROUPS_LDAP_USER_SCHEMA_ID
-  defaultValue: ownclouduuid
+  name: OCIS_LDAP_USER_SCHEMA_ID;IDP_LDAP_UUID_ATTRIBUTE
+  defaultValue: ownCloudUUID
   type: string
-  description: LDAP Attribute to use as the unique id for users. This should be a
-    stable globally unique id like a UUID.
+  description: LDAP User UUID attribute like 'uid'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING:
-  name: OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;GROUPS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING
+  name: OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;USERS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING
   defaultValue: "false"
   type: bool
   description: Set this to true if the defined 'ID' attribute for users is of the
@@ -8405,16 +8331,16 @@ OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_MAIL:
-  name: OCIS_LDAP_USER_SCHEMA_MAIL;GROUPS_LDAP_USER_SCHEMA_MAIL
+  name: OCIS_LDAP_USER_SCHEMA_MAIL;IDP_LDAP_EMAIL_ATTRIBUTE
   defaultValue: mail
   type: string
-  description: LDAP Attribute to use for the email address of users.
+  description: LDAP User email attribute like 'mail'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_USER_TYPE:
-  name: OCIS_LDAP_USER_SCHEMA_USER_TYPE;GRAPH_LDAP_USER_TYPE_ATTRIBUTE
+  name: OCIS_LDAP_USER_SCHEMA_USER_TYPE;USERS_LDAP_USER_TYPE_ATTRIBUTE
   defaultValue: ownCloudUserType
   type: string
   description: LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default
@@ -8424,16 +8350,16 @@ OCIS_LDAP_USER_SCHEMA_USER_TYPE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCHEMA_USERNAME:
-  name: OCIS_LDAP_USER_SCHEMA_USERNAME;GROUPS_LDAP_USER_SCHEMA_USERNAME
-  defaultValue: uid
+  name: OCIS_LDAP_USER_SCHEMA_USERNAME;IDP_LDAP_NAME_ATTRIBUTE
+  defaultValue: displayName
   type: string
-  description: LDAP Attribute to use for username of users.
+  description: LDAP User name attribute like 'displayName'.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LDAP_USER_SCOPE:
-  name: OCIS_LDAP_USER_SCOPE;GROUPS_LDAP_USER_SCOPE
+  name: OCIS_LDAP_USER_SCOPE;IDP_LDAP_SCOPE
   defaultValue: sub
   type: string
   description: LDAP search scope to use when looking up users. Supported scopes are
@@ -8443,7 +8369,7 @@ OCIS_LDAP_USER_SCOPE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LOG_COLOR:
-  name: OCIS_LOG_COLOR;STORAGE_SHARES_LOG_COLOR
+  name: OCIS_LOG_COLOR;PROXY_LOG_COLOR
   defaultValue: "false"
   type: bool
   description: Activates colorized log output.
@@ -8452,7 +8378,7 @@ OCIS_LOG_COLOR:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LOG_FILE:
-  name: OCIS_LOG_FILE;STORAGE_SHARES_LOG_FILE
+  name: OCIS_LOG_FILE;PROXY_LOG_FILE
   defaultValue: ""
   type: string
   description: The path to the log file. Activates logging to this file if set.
@@ -8461,7 +8387,7 @@ OCIS_LOG_FILE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LOG_LEVEL:
-  name: OCIS_LOG_LEVEL;STORAGE_SHARES_LOG_LEVEL
+  name: OCIS_LOG_LEVEL;PROXY_LOG_LEVEL
   defaultValue: ""
   type: string
   description: 'The log level. Valid values are: ''panic'', ''fatal'', ''error'',
@@ -8471,7 +8397,7 @@ OCIS_LOG_LEVEL:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_LOG_PRETTY:
-  name: OCIS_LOG_PRETTY;STORAGE_SHARES_LOG_PRETTY
+  name: OCIS_LOG_PRETTY;PROXY_LOG_PRETTY
   defaultValue: "false"
   type: bool
   description: Activates pretty log output.
@@ -8480,11 +8406,11 @@ OCIS_LOG_PRETTY:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_MACHINE_AUTH_API_KEY:
-  name: OCIS_MACHINE_AUTH_API_KEY;AUTH_MACHINE_API_KEY
+  name: OCIS_MACHINE_AUTH_API_KEY;PROXY_MACHINE_AUTH_API_KEY
   defaultValue: ""
   type: string
-  description: Machine auth API key used to validate internal requests necessary for
-    the access to resources from other services.
+  description: Machine auth API key used to validate internal requests necessary to
+    access resources from other services.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
@@ -8501,17 +8427,16 @@ OCIS_OIDC_CLIENT_ID:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_OIDC_ISSUER:
-  name: OCIS_URL;OCIS_OIDC_ISSUER;GROUPS_IDP_URL
+  name: OCIS_URL;OCIS_OIDC_ISSUER;PROXY_OIDC_ISSUER
   defaultValue: https://localhost:9200
   type: string
-  description: The identity provider value to set in the group IDs of the CS3 group
-    objects for groups returned by this group provider.
+  description: URL of the OIDC issuer. It defaults to URL of the builtin IDP.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST:
-  name: OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST
+  name: OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;SHARING_PASSWORD_POLICY_BANNED_PASSWORDS_LIST
   defaultValue: ""
   type: string
   description: Path to the 'banned passwords list' file. This only impacts public
@@ -8521,7 +8446,7 @@ OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_DISABLED:
-  name: OCIS_PASSWORD_POLICY_DISABLED;FRONTEND_PASSWORD_POLICY_DISABLED
+  name: OCIS_PASSWORD_POLICY_DISABLED;SHARING_PASSWORD_POLICY_DISABLED
   defaultValue: "false"
   type: bool
   description: Disable the password policy. Defaults to false if not set.
@@ -8530,7 +8455,7 @@ OCIS_PASSWORD_POLICY_DISABLED:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_MIN_CHARACTERS:
-  name: OCIS_PASSWORD_POLICY_MIN_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS
+  name: OCIS_PASSWORD_POLICY_MIN_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_CHARACTERS
   defaultValue: "8"
   type: int
   description: Define the minimum password length. Defaults to 8 if not set.
@@ -8539,7 +8464,7 @@ OCIS_PASSWORD_POLICY_MIN_CHARACTERS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_MIN_DIGITS:
-  name: OCIS_PASSWORD_POLICY_MIN_DIGITS;FRONTEND_PASSWORD_POLICY_MIN_DIGITS
+  name: OCIS_PASSWORD_POLICY_MIN_DIGITS;SHARING_PASSWORD_POLICY_MIN_DIGITS
   defaultValue: "1"
   type: int
   description: Define the minimum number of digits. Defaults to 1 if not set.
@@ -8548,7 +8473,7 @@ OCIS_PASSWORD_POLICY_MIN_DIGITS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS:
-  name: OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS
+  name: OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS
   defaultValue: "1"
   type: int
   description: Define the minimum number of uppercase letters. Defaults to 1 if not
@@ -8558,7 +8483,7 @@ OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS:
-  name: OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS
+  name: OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS
   defaultValue: "1"
   type: int
   description: Define the minimum number of characters from the special characters
@@ -8568,7 +8493,7 @@ OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS:
-  name: OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS
+  name: OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS
   defaultValue: "1"
   type: int
   description: Define the minimum number of lowercase letters. Defaults to 1 if not
@@ -8578,8 +8503,8 @@ OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE:
-  name: OCIS_PERSISTENT_STORE;USERLOG_STORE
-  defaultValue: memory
+  name: OCIS_PERSISTENT_STORE;POSTPROCESSING_STORE
+  defaultValue: nats-js-kv
   type: string
   description: 'The type of the store. Supported values are: ''memory'', ''ocmem'',
     ''etcd'', ''redis'', ''redis-sentinel'', ''nats-js'', ''noop''. See the text description
@@ -8589,7 +8514,7 @@ OCIS_PERSISTENT_STORE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE_AUTH_PASSWORD:
-  name: OCIS_PERSISTENT_STORE_AUTH_PASSWORD;USERLOG_STORE_AUTH_PASSWORD
+  name: OCIS_PERSISTENT_STORE_AUTH_PASSWORD;POSTPROCESSING_STORE_AUTH_PASSWORD
   defaultValue: ""
   type: string
   description: The password to authenticate with the store. Only applies when store
@@ -8599,7 +8524,7 @@ OCIS_PERSISTENT_STORE_AUTH_PASSWORD:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE_AUTH_USERNAME:
-  name: OCIS_PERSISTENT_STORE_AUTH_USERNAME;USERLOG_STORE_AUTH_USERNAME
+  name: OCIS_PERSISTENT_STORE_AUTH_USERNAME;POSTPROCESSING_STORE_AUTH_USERNAME
   defaultValue: ""
   type: string
   description: The username to authenticate with the store. Only applies when store
@@ -8609,8 +8534,8 @@ OCIS_PERSISTENT_STORE_AUTH_USERNAME:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE_NODES:
-  name: OCIS_PERSISTENT_STORE_NODES;USERLOG_STORE_NODES
-  defaultValue: '[]'
+  name: OCIS_PERSISTENT_STORE_NODES;POSTPROCESSING_STORE_NODES
+  defaultValue: '[127.0.0.1:9233]'
   type: '[]string'
   description: A list of nodes to access the configured store. This has no effect
     when 'memory' or 'ocmem' stores are configured. Note that the behaviour how nodes
@@ -8621,7 +8546,7 @@ OCIS_PERSISTENT_STORE_NODES:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE_SIZE:
-  name: OCIS_PERSISTENT_STORE_SIZE;USERLOG_STORE_SIZE
+  name: OCIS_PERSISTENT_STORE_SIZE;POSTPROCESSING_STORE_SIZE
   defaultValue: "0"
   type: int
   description: The maximum quantity of items in the store. Only applies when store
@@ -8632,11 +8557,11 @@ OCIS_PERSISTENT_STORE_SIZE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_PERSISTENT_STORE_TTL:
-  name: OCIS_PERSISTENT_STORE_TTL;USERLOG_STORE_TTL
-  defaultValue: 336h0m0s
+  name: OCIS_PERSISTENT_STORE_TTL;POSTPROCESSING_STORE_TTL
+  defaultValue: 0s
   type: Duration
-  description: Time to live for events in the store. Defaults to '336h' (2 weeks).
-    See the Environment Variable Types description for more details.
+  description: Time to live for events in the store. See the Environment Variable
+    Types description for more details.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
@@ -8682,7 +8607,7 @@ OCIS_REVA_GATEWAY_TLS_MODE:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SERVICE_ACCOUNT_ID:
-  name: OCIS_SERVICE_ACCOUNT_ID;SEARCH_SERVICE_ACCOUNT_ID
+  name: OCIS_SERVICE_ACCOUNT_ID;PROXY_SERVICE_ACCOUNT_ID
   defaultValue: ""
   type: string
   description: The ID of the service account the service should use. See the 'auth-service'
@@ -8692,7 +8617,7 @@ OCIS_SERVICE_ACCOUNT_ID:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SERVICE_ACCOUNT_SECRET:
-  name: OCIS_SERVICE_ACCOUNT_SECRET;SEARCH_SERVICE_ACCOUNT_SECRET
+  name: OCIS_SERVICE_ACCOUNT_SECRET;PROXY_SERVICE_ACCOUNT_SECRET
   defaultValue: ""
   type: string
   description: The service account secret.
@@ -8701,7 +8626,7 @@ OCIS_SERVICE_ACCOUNT_SECRET:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD:
-  name: OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_SHARE_MUST_HAVE_PASSWORD
+  name: OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD
   defaultValue: "true"
   type: bool
   description: Set this to true if you want to enforce passwords on all public shares.
@@ -8710,11 +8635,13 @@ OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD:
-  name: OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD
+  name: OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD
   defaultValue: "false"
   type: bool
-  description: Set this to true if you want to enforce passwords for writable shares.
-    Only effective if the setting for 'passwords on all public shares' is set to false.
+  description: Set this to true if you want to enforce passwords on Uploader, Editor
+    or Contributor shares. If not using the global OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD,
+    you must define the FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD in
+    the frontend service.
   introductionVersion: "5.0"
   deprecationVersion: ""
   removalVersion: ""
@@ -8731,11 +8658,12 @@ OCIS_SHOW_USER_EMAIL_IN_RESULTS:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SPACES_MAX_QUOTA:
-  name: OCIS_SPACES_MAX_QUOTA;FRONTEND_MAX_QUOTA
+  name: OCIS_SPACES_MAX_QUOTA;STORAGE_USERS_OCIS_MAX_QUOTA
   defaultValue: "0"
   type: uint64
-  description: Set the global max quota value in bytes. A value of 0 equals unlimited.
-    The value is provided via capabilities.
+  description: Set a global max quota for spaces in bytes. A value of 0 equals unlimited.
+    If not using the global OCIS_SPACES_MAX_QUOTA, you must define the FRONTEND_MAX_QUOTA
+    in the frontend service.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
@@ -8750,10 +8678,10 @@ OCIS_SYSTEM_USER_API_KEY:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SYSTEM_USER_ID:
-  name: OCIS_SYSTEM_USER_ID;SETTINGS_SYSTEM_USER_ID
+  name: OCIS_SYSTEM_USER_ID
   defaultValue: ""
   type: string
-  description: ID of the oCIS STORAGE-SYSTEM system user. Admins need to set the ID
+  description: ID of the oCIS storage-system system user. Admins need to set the ID
     for the STORAGE-SYSTEM system user in this config option which is then used to
     reference the user. Any reasonable long string is possible, preferably this would
     be an UUIDv4 format.
@@ -8762,7 +8690,7 @@ OCIS_SYSTEM_USER_ID:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_SYSTEM_USER_IDP:
-  name: OCIS_SYSTEM_USER_IDP;SETTINGS_SYSTEM_USER_IDP
+  name: OCIS_SYSTEM_USER_IDP;SHARING_PUBLIC_CS3_SYSTEM_USER_IDP
   defaultValue: internal
   type: string
   description: IDP of the oCIS STORAGE-SYSTEM system user.
@@ -8771,7 +8699,7 @@ OCIS_SYSTEM_USER_IDP:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_TRACING_COLLECTOR:
-  name: OCIS_TRACING_COLLECTOR;STORAGE_SHARES_TRACING_COLLECTOR
+  name: OCIS_TRACING_COLLECTOR;PROXY_TRACING_COLLECTOR
   defaultValue: ""
   type: string
   description: The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces.
@@ -8781,7 +8709,7 @@ OCIS_TRACING_COLLECTOR:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_TRACING_ENABLED:
-  name: OCIS_TRACING_ENABLED;STORAGE_SHARES_TRACING_ENABLED
+  name: OCIS_TRACING_ENABLED;PROXY_TRACING_ENABLED
   defaultValue: "false"
   type: bool
   description: Activates tracing.
@@ -8790,7 +8718,7 @@ OCIS_TRACING_ENABLED:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_TRACING_ENDPOINT:
-  name: OCIS_TRACING_ENDPOINT;STORAGE_SHARES_TRACING_ENDPOINT
+  name: OCIS_TRACING_ENDPOINT;PROXY_TRACING_ENDPOINT
   defaultValue: ""
   type: string
   description: The endpoint of the tracing agent.
@@ -8799,7 +8727,7 @@ OCIS_TRACING_ENDPOINT:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_TRACING_TYPE:
-  name: OCIS_TRACING_TYPE;STORAGE_SHARES_TRACING_TYPE
+  name: OCIS_TRACING_TYPE;PROXY_TRACING_TYPE
   defaultValue: ""
   type: string
   description: The type of tracing. Defaults to '', which is the same as 'jaeger'.
@@ -8812,7 +8740,7 @@ OCIS_TRANSFER_SECRET:
   name: OCIS_TRANSFER_SECRET
   defaultValue: ""
   type: string
-  description: Transfer secret for signing file up- and download requests.
+  description: The storage transfer secret.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
@@ -8829,21 +8757,20 @@ OCIS_TRANSLATION_PATH:
   removalVersion: ""
   deprecationInfo: ""
 OCIS_URL:
-  name: OCIS_URL;APP_PROVIDER_WOPI_FOLDER_URL_BASE_URL
-  defaultValue: https://localhost:9200/
+  name: OCIS_URL;OCIS_OIDC_ISSUER;PROXY_OIDC_ISSUER
+  defaultValue: https://localhost:9200
   type: string
-  description: Base url to navigate back from the app to the containing folder in
-    the file list.
+  description: URL of the OIDC issuer. It defaults to URL of the builtin IDP.
   introductionVersion: pre5.0
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
 OCIS_WOPI_DISABLE_CHAT:
-  name: APP_PROVIDER_WOPI_DISABLE_CHAT;OCIS_WOPI_DISABLE_CHAT
+  name: COLLABORATION_WOPI_DISABLE_CHAT;OCIS_WOPI_DISABLE_CHAT
   defaultValue: "false"
   type: bool
-  description: Disable the chat functionality of the office app.
-  introductionVersion: pre5.0
+  description: Disable chat in the frontend.
+  introductionVersion: '%%NEXT%%'
   deprecationVersion: ""
   removalVersion: ""
   deprecationInfo: ""
diff --git a/docs/helpers/extended_vars.yaml b/docs/helpers/extended_vars.yaml
index 82a745f9190..1e2bd86787d 100644
--- a/docs/helpers/extended_vars.yaml
+++ b/docs/helpers/extended_vars.yaml
@@ -15,32 +15,6 @@ variables:
   default_value: ""
   description: ""
   do_ignore: true
-- rawname: _registryRegisterIntervalEnv
-  path: ocis-pkg/registry/expiry.go:19
-  foundincode: true
-  name: EXPERIMENTAL_REGISTER_INTERVAL
-  type: ""
-  default_value: ""
-  description: ""
-  do_ignore: true
-- rawname: _registryRegisterTTLEnv
-  path: ocis-pkg/registry/expiry.go:28
-  foundincode: true
-  name: EXPERIMENTAL_REGISTER_TTL
-  type: ""
-  default_value: ""
-  description: ""
-  do_ignore: true
-- rawname: _serverMaxConnectionAgeEnv
-  path: ocis-pkg/service/grpc/keepalive.go:19
-  foundincode: true
-  name: GRPC_MAX_CONNECTION_AGE
-  type: Duration
-  default_value: "9223372036854775807"
-  description: Timeout for GRPC connections. After timeout, a new connection will
-    be established automatically. The default value is in ns and is about 2.5 mio
-    h.
-  do_ignore: false
 - rawname: MICRO_LOG_LEVEL
   path: ocis-pkg/log/log.go:34
   foundincode: true
@@ -78,7 +52,7 @@ variables:
     on supervision of ownCloud Support.'
   do_ignore: false
 - rawname: _registryAddressEnv
-  path: ocis-pkg/natsjsregistry/registry.go:192
+  path: ocis-pkg/natsjsregistry/registry.go:186
   foundincode: true
   name: MICRO_REGISTRY_ADDRESS
   type: string
@@ -95,7 +69,7 @@ variables:
   description: ""
   do_ignore: true
 - rawname: _registryPasswordEnv
-  path: ocis-pkg/natsjsregistry/registry.go:220
+  path: ocis-pkg/natsjsregistry/registry.go:214
   foundincode: true
   name: MICRO_REGISTRY_AUTH_PASSWORD
   type: string
@@ -103,7 +77,7 @@ variables:
   description: Optional when using nats to authenticate with the nats cluster.
   do_ignore: false
 - rawname: _registryUsernameEnv
-  path: ocis-pkg/natsjsregistry/registry.go:220
+  path: ocis-pkg/natsjsregistry/registry.go:214
   foundincode: true
   name: MICRO_REGISTRY_AUTH_USERNAME
   type: string
diff --git a/services/graph/pkg/identity/backend.go b/services/graph/pkg/identity/backend.go
index 799430128ee..8d8d25bb638 100644
--- a/services/graph/pkg/identity/backend.go
+++ b/services/graph/pkg/identity/backend.go
@@ -19,12 +19,6 @@ var (
 	ErrNotFound = errorcode.New(errorcode.ItemNotFound, "not found")
 )
 
-const (
-	UserTypeMember    = "Member"
-	UserTypeGuest     = "Guest"
-	UserTypeFederated = "Federated"
-)
-
 // Backend defines the Interface for an IdentityBackend implementation
 type Backend interface {
 	// CreateUser creates a given user in the identity backend.
@@ -112,10 +106,9 @@ type EducationBackend interface {
 
 // CreateUserModelFromCS3 converts a cs3 User object into a libregraph.User
 func CreateUserModelFromCS3(u *cs3user.User) *libregraph.User {
-	if u.GetId() == nil {
+	if u.Id == nil {
 		u.Id = &cs3user.UserId{}
 	}
-	userType := cs3UserTypeToGraph(u.GetId().GetType())
 	return &libregraph.User{
 		Identities: []libregraph.ObjectIdentity{
 			{
@@ -123,7 +116,6 @@ func CreateUserModelFromCS3(u *cs3user.User) *libregraph.User {
 				IssuerAssignedId: &u.GetId().OpaqueId,
 			},
 		},
-		UserType:                 &userType,
 		DisplayName:              &u.DisplayName,
 		Mail:                     &u.Mail,
 		OnPremisesSamAccountName: &u.Username,
@@ -131,21 +123,9 @@ func CreateUserModelFromCS3(u *cs3user.User) *libregraph.User {
 	}
 }
 
-func cs3UserTypeToGraph(cs3type cs3user.UserType) string {
-	switch cs3type {
-	case cs3user.UserType_USER_TYPE_PRIMARY:
-		return UserTypeMember
-	case cs3user.UserType_USER_TYPE_FEDERATED:
-		return UserTypeFederated
-	case cs3user.UserType_USER_TYPE_GUEST:
-		return UserTypeGuest
-	}
-	return "unknown"
-}
-
 // CreateGroupModelFromCS3 converts a cs3 Group object into a libregraph.Group
 func CreateGroupModelFromCS3(g *cs3group.Group) *libregraph.Group {
-	if g.GetId() == nil {
+	if g.Id == nil {
 		g.Id = &cs3group.GroupId{}
 	}
 	return &libregraph.Group{
diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go
index 37f164d986e..dea9f3a6d7e 100644
--- a/services/graph/pkg/identity/ldap.go
+++ b/services/graph/pkg/identity/ldap.go
@@ -810,14 +810,9 @@ func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
 			Id:                       &id,
 			GivenName:                pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName)),
 			Surname:                  &surname,
+			UserType:                 pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.userType)),
 			AccountEnabled:           booleanOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.accountEnabled)),
 		}
-
-		userType := e.GetEqualFoldAttributeValue(i.userAttributeMap.userType)
-		if userType == "" {
-			userType = UserTypeMember
-		}
-		user.SetUserType(userType)
 		var identities []libregraph.ObjectIdentity
 		for _, identityStr := range e.GetEqualFoldAttributeValues(i.userAttributeMap.identities) {
 			parts := strings.SplitN(identityStr, "$", 3)


Test patch:
diff --git a/services/graph/pkg/identity/ldap_test.go b/services/graph/pkg/identity/ldap_test.go
index 8d38ec83f98..36630cbf759 100644
--- a/services/graph/pkg/identity/ldap_test.go
+++ b/services/graph/pkg/identity/ldap_test.go
@@ -397,7 +397,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
 				accountEnabled:           nil,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -527,7 +526,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "newName",
 				onPremisesSamAccountName: "testUser",
 				accountEnabled:           nil,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -657,7 +655,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "newName",
 				onPremisesSamAccountName: "newName",
 				accountEnabled:           nil,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -847,7 +844,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
 				accountEnabled:           &falseBool,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -978,7 +974,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
 				accountEnabled:           &falseBool,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
@@ -1145,7 +1140,6 @@ func TestUpdateUser(t *testing.T) {
 				displayName:              "testUser",
 				onPremisesSamAccountName: "testUser",
 				accountEnabled:           &trueBool,
-				userType:                 &memberType,
 			},
 			assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
 				return assert.Nil(t, err, args...)
diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go
index c8f7b18c9ec..0cd2fbe7ccd 100644
--- a/services/graph/pkg/service/v0/users_test.go
+++ b/services/graph/pkg/service/v0/users_test.go
@@ -11,13 +11,8 @@ import (
 
 	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
 	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
-	invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
 	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
-	revactx "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
-	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
-	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
 	"github.com/go-chi/chi/v5"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -26,6 +21,10 @@ import (
 	"go-micro.dev/v4/client"
 	"google.golang.org/grpc"
 
+	revactx "github.com/cs3org/reva/v2/pkg/ctx"
+	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
+	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
+	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
 	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
 	settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
@@ -91,1079 +90,1019 @@ var _ = Describe("Users", func() {
 		cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
 		cfg.Application.ID = "some-application-ID"
 
+		svc, _ = service.NewService(
+			service.Config(cfg),
+			service.WithGatewaySelector(gatewaySelector),
+			service.EventsPublisher(&eventsPublisher),
+			service.WithIdentityBackend(identityBackend),
+			service.WithRoleService(roleService),
+			service.WithValueService(valueService),
+			service.PermissionService(permissionService),
+		)
 	})
 
-	When("OCM is disabled", func() {
-		BeforeEach(func() {
-			svc, _ = service.NewService(
-				service.Config(cfg),
-				service.WithGatewaySelector(gatewaySelector),
-				service.EventsPublisher(&eventsPublisher),
-				service.WithIdentityBackend(identityBackend),
-				service.WithRoleService(roleService),
-				service.WithValueService(valueService),
-				service.PermissionService(permissionService),
-			)
-		})
-
-		Describe("GetMe", func() {
-			It("handles missing user", func() {
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
-				svc.GetMe(rr, r)
+	Describe("GetMe", func() {
+		It("handles missing user", func() {
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
+			svc.GetMe(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusInternalServerError))
-			})
+			Expect(rr.Code).To(Equal(http.StatusInternalServerError))
+		})
 
-			It("gets the information", func() {
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+		It("gets the information", func() {
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
+					},
+				}, nil)
 
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
-				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.GetMe(rr, r)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.GetMe(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-			})
+			Expect(rr.Code).To(Equal(http.StatusOK))
+		})
 
-			It("expands the memberOf", func() {
-				user := &libregraph.User{
-					Id: libregraph.PtrString("user1"),
-					MemberOf: []libregraph.Group{
-						{DisplayName: libregraph.PtrString("somegroup")},
-					},
-				}
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+		It("expands the memberOf", func() {
+			user := &libregraph.User{
+				Id: libregraph.PtrString("user1"),
+				MemberOf: []libregraph.Group{
+					{DisplayName: libregraph.PtrString("somegroup")},
+				},
+			}
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=memberOf", nil)
-				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.GetMe(rr, r)
+					},
+				}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=memberOf", nil)
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.GetMe(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
 
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
 
-				Expect(responseUser.GetId()).To(Equal("user1"))
-				Expect(responseUser.GetMemberOf()).To(HaveLen(1))
-				Expect(responseUser.GetMemberOf()[0].GetDisplayName()).To(Equal("somegroup"))
+			Expect(responseUser.GetId()).To(Equal("user1"))
+			Expect(responseUser.GetMemberOf()).To(HaveLen(1))
+			Expect(responseUser.GetMemberOf()[0].GetDisplayName()).To(Equal("somegroup"))
 
-			})
+		})
 
-			It("expands the appRoleAssignments", func() {
-				assignments := []*settingsmsg.UserRoleAssignment{
-					{
-						Id:          "some-appRoleAssignment-ID",
-						AccountUuid: "user",
-						RoleId:      "some-appRole-ID",
-					},
-				}
-				roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+		It("expands the appRoleAssignments", func() {
+			assignments := []*settingsmsg.UserRoleAssignment{
+				{
+					Id:          "some-appRoleAssignment-ID",
+					AccountUuid: "user",
+					RoleId:      "some-appRole-ID",
+				},
+			}
+			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=appRoleAssignments", nil)
-				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.GetMe(rr, r)
+					},
+				}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=appRoleAssignments", nil)
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.GetMe(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
+			Expect(rr.Code).To(Equal(http.StatusOK))
 
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
 
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
 
-				Expect(responseUser.GetId()).To(Equal("user"))
-				Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-			})
+			Expect(responseUser.GetId()).To(Equal("user"))
+			Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
 		})
+	})
 
-		Describe("GetUsers", func() {
-			It("handles invalid requests", func() {
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$invalid=true", nil)
-				svc.GetUsers(rr, r)
+	Describe("GetUsers", func() {
+		It("handles invalid requests", func() {
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$invalid=true", nil)
+			svc.GetUsers(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-			It("lists the users", func() {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
-					},
-				}, nil)
+		It("lists the users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
+				Permission: &settingsmsg.Permission{
+					Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
+					Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+				},
+			}, nil)
 
-				user := &libregraph.User{}
-				user.SetId("user1")
-				users := []*libregraph.User{user}
+			user := &libregraph.User{}
+			user.SetId("user1")
+			users := []*libregraph.User{user}
 
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
+			identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
 
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
-				svc.GetUsers(rr, r)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
+			svc.GetUsers(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
 
-				res := userList{}
-				err = json.Unmarshal(data, &res)
-				Expect(err).ToNot(HaveOccurred())
-
-				Expect(len(res.Value)).To(Equal(1))
-				Expect(res.Value[0].GetId()).To(Equal("user1"))
-			})
-			It("denies listing for unprivileged users", func() {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
-				svc.GetUsers(rr, r)
+			res := userList{}
+			err = json.Unmarshal(data, &res)
+			Expect(err).ToNot(HaveOccurred())
 
-				Expect(rr.Code).To(Equal(http.StatusForbidden))
-			})
-			It("denies using to short search terms for unprivileged users", func() {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=a", nil)
-				svc.GetUsers(rr, r)
+			Expect(len(res.Value)).To(Equal(1))
+			Expect(res.Value[0].GetId()).To(Equal("user1"))
+		})
+		It("denies listing for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
+			svc.GetUsers(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusForbidden))
-			})
-			It("denies using to short quoted search terms for unprivileged users", func() {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=%22ab%22", nil)
-				svc.GetUsers(rr, r)
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
+		It("denies using to short search terms for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=a", nil)
+			svc.GetUsers(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusForbidden))
-			})
-			It("only returns a restricted set of attributes for unprivileged users", func() {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
-				user := &libregraph.User{}
-				user.SetId("user1")
-				user.SetPasswordProfile(
-					libregraph.PasswordProfile{
-						Password: libregraph.PtrString("password"),
-					},
-				)
-				user.SetUserType("usertype")
-				user.SetDisplayName("abc user")
-				users := []*libregraph.User{user}
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
+		It("denies using to short quoted search terms for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=%22ab%22", nil)
+			svc.GetUsers(rr, r)
 
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=abc", nil)
-				svc.GetUsers(rr, r)
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
+		It("only returns a restricted set of attributes for unprivileged users", func() {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
+			user := &libregraph.User{}
+			user.SetId("user1")
+			user.SetPasswordProfile(
+				libregraph.PasswordProfile{
+					Password: libregraph.PtrString("password"),
+				},
+			)
+			user.SetUserType("usertype")
+			user.SetDisplayName("abc user")
+			users := []*libregraph.User{user}
+
+			identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=abc", nil)
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			res := userList{}
+			err = json.Unmarshal(data, &res)
+			Expect(err).ToNot(HaveOccurred())
+			userMap, err := res.Value[0].ToMap()
+			Expect(err).ToNot(HaveOccurred())
+			for k, _ := range userMap {
+				Expect(k).Should(BeElementOf([]string{"mail", "displayName", "id", "userType"}))
+			}
+		})
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
+		It("sorts", func() {
+			user := &libregraph.User{}
+			user.SetId("user1")
+			user.SetMail("z@example.com")
+			user.SetDisplayName("9")
+			user.SetOnPremisesSamAccountName("9")
+			user2 := &libregraph.User{}
+			user2.SetId("user2")
+			user2.SetMail("a@example.com")
+			user2.SetDisplayName("1")
+			user2.SetOnPremisesSamAccountName("1")
+			users := []*libregraph.User{user, user2}
+
+			identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
+
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
+				Permission: &settingsmsg.Permission{
+					Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
+					Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+				},
+			}, nil)
+
+			getUsers := func(path string) []*libregraph.User {
+				r := httptest.NewRequest(http.MethodGet, path, nil)
+				rec := httptest.NewRecorder()
+				svc.GetUsers(rec, r)
+
+				Expect(rec.Code).To(Equal(http.StatusOK))
+				data, err := io.ReadAll(rec.Body)
 				Expect(err).ToNot(HaveOccurred())
 
 				res := userList{}
 				err = json.Unmarshal(data, &res)
 				Expect(err).ToNot(HaveOccurred())
-				userMap, err := res.Value[0].ToMap()
-				Expect(err).ToNot(HaveOccurred())
-				for k, _ := range userMap {
-					Expect(k).Should(BeElementOf([]string{"mail", "displayName", "id", "userType"}))
-				}
-			})
+				return res.Value
+			}
+
+			unsorted := getUsers("/graph/v1.0/users")
+			Expect(len(unsorted)).To(Equal(2))
+			Expect(unsorted[0].GetId()).To(Equal("user1"))
+			Expect(unsorted[1].GetId()).To(Equal("user2"))
+
+			byMail := getUsers("/graph/v1.0/users?$orderby=mail")
+			Expect(len(byMail)).To(Equal(2))
+			Expect(byMail[0].GetId()).To(Equal("user2"))
+			Expect(byMail[1].GetId()).To(Equal("user1"))
+			byMail = getUsers("/graph/v1.0/users?$orderby=mail%20asc")
+			Expect(len(byMail)).To(Equal(2))
+			Expect(byMail[0].GetId()).To(Equal("user2"))
+			Expect(byMail[1].GetId()).To(Equal("user1"))
+			byMail = getUsers("/graph/v1.0/users?$orderby=mail%20desc")
+			Expect(len(byMail)).To(Equal(2))
+			Expect(byMail[0].GetId()).To(Equal("user1"))
+			Expect(byMail[1].GetId()).To(Equal("user2"))
+
+			byDisplayName := getUsers("/graph/v1.0/users?$orderby=displayName")
+			Expect(len(byDisplayName)).To(Equal(2))
+			Expect(byDisplayName[0].GetId()).To(Equal("user2"))
+			Expect(byDisplayName[1].GetId()).To(Equal("user1"))
+			byDisplayName = getUsers("/graph/v1.0/users?$orderby=displayName%20asc")
+			Expect(len(byDisplayName)).To(Equal(2))
+			Expect(byDisplayName[0].GetId()).To(Equal("user2"))
+			Expect(byDisplayName[1].GetId()).To(Equal("user1"))
+			byDisplayName = getUsers("/graph/v1.0/users?$orderby=displayName%20desc")
+			Expect(len(byDisplayName)).To(Equal(2))
+			Expect(byDisplayName[0].GetId()).To(Equal("user1"))
+			Expect(byDisplayName[1].GetId()).To(Equal("user2"))
+
+			byOnPremisesSamAccountName := getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName")
+			Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
+			Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
+			Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
+			byOnPremisesSamAccountName = getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName%20asc")
+			Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
+			Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
+			Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
+			byOnPremisesSamAccountName = getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName%20desc")
+			Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
+			Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user1"))
+			Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user2"))
+
+			// Handles invalid sort field
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$orderby=invalid", nil)
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-			It("sorts", func() {
-				user := &libregraph.User{}
-				user.SetId("user1")
-				user.SetMail("z@example.com")
-				user.SetDisplayName("9")
-				user.SetOnPremisesSamAccountName("9")
-				user2 := &libregraph.User{}
-				user2.SetId("user2")
-				user2.SetMail("a@example.com")
-				user2.SetDisplayName("1")
-				user2.SetOnPremisesSamAccountName("1")
-				users := []*libregraph.User{user, user2}
-
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
-
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+		It("expands the appRoleAssignments", func() {
+
+			user := &libregraph.User{}
+			user.SetId("user1")
+			user.SetMail("z@example.com")
+			user.SetDisplayName("9")
+			user.SetOnPremisesSamAccountName("9")
+			user2 := &libregraph.User{}
+			user2.SetId("user2")
+			user2.SetMail("a@example.com")
+			user2.SetDisplayName("1")
+			user2.SetOnPremisesSamAccountName("1")
+			users := []*libregraph.User{user, user2}
+			identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
+
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
+				Permission: &settingsmsg.Permission{
+					Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
+					Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+				},
+			}, nil)
+			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
+				return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
+					{
+						Id:          "some-appRoleAssignment-ID",
+						AccountUuid: in.GetAccountUuid(),
+						RoleId:      "some-appRole-ID",
 					},
-				}, nil)
-
-				getUsers := func(path string) []*libregraph.User {
-					r := httptest.NewRequest(http.MethodGet, path, nil)
-					rec := httptest.NewRecorder()
-					svc.GetUsers(rec, r)
+				}}
+			}, nil)
+
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=appRoleAssignments", nil)
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			res := userList{}
+			err = json.Unmarshal(data, &res)
+			Expect(err).ToNot(HaveOccurred())
+
+			responseUsers := res.Value
+			Expect(len(responseUsers)).To(Equal(2))
+			Expect(responseUsers[0].GetId()).To(Equal("user1"))
+			Expect(responseUsers[0].GetAppRoleAssignments()).To(HaveLen(1))
+			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
+			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
+			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
+			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
+
+			Expect(responseUsers[1].GetId()).To(Equal("user2"))
+			Expect(responseUsers[1].GetAppRoleAssignments()).To(HaveLen(1))
+			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
+			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
+			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user2"))
+			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
 
-					Expect(rec.Code).To(Equal(http.StatusOK))
-					data, err := io.ReadAll(rec.Body)
-					Expect(err).ToNot(HaveOccurred())
-
-					res := userList{}
-					err = json.Unmarshal(data, &res)
-					Expect(err).ToNot(HaveOccurred())
-					return res.Value
-				}
-
-				unsorted := getUsers("/graph/v1.0/users")
-				Expect(len(unsorted)).To(Equal(2))
-				Expect(unsorted[0].GetId()).To(Equal("user1"))
-				Expect(unsorted[1].GetId()).To(Equal("user2"))
-
-				byMail := getUsers("/graph/v1.0/users?$orderby=mail")
-				Expect(len(byMail)).To(Equal(2))
-				Expect(byMail[0].GetId()).To(Equal("user2"))
-				Expect(byMail[1].GetId()).To(Equal("user1"))
-				byMail = getUsers("/graph/v1.0/users?$orderby=mail%20asc")
-				Expect(len(byMail)).To(Equal(2))
-				Expect(byMail[0].GetId()).To(Equal("user2"))
-				Expect(byMail[1].GetId()).To(Equal("user1"))
-				byMail = getUsers("/graph/v1.0/users?$orderby=mail%20desc")
-				Expect(len(byMail)).To(Equal(2))
-				Expect(byMail[0].GetId()).To(Equal("user1"))
-				Expect(byMail[1].GetId()).To(Equal("user2"))
-
-				byDisplayName := getUsers("/graph/v1.0/users?$orderby=displayName")
-				Expect(len(byDisplayName)).To(Equal(2))
-				Expect(byDisplayName[0].GetId()).To(Equal("user2"))
-				Expect(byDisplayName[1].GetId()).To(Equal("user1"))
-				byDisplayName = getUsers("/graph/v1.0/users?$orderby=displayName%20asc")
-				Expect(len(byDisplayName)).To(Equal(2))
-				Expect(byDisplayName[0].GetId()).To(Equal("user2"))
-				Expect(byDisplayName[1].GetId()).To(Equal("user1"))
-				byDisplayName = getUsers("/graph/v1.0/users?$orderby=displayName%20desc")
-				Expect(len(byDisplayName)).To(Equal(2))
-				Expect(byDisplayName[0].GetId()).To(Equal("user1"))
-				Expect(byDisplayName[1].GetId()).To(Equal("user2"))
-
-				byOnPremisesSamAccountName := getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName")
-				Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
-				Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
-				Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
-				byOnPremisesSamAccountName = getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName%20asc")
-				Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
-				Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
-				Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
-				byOnPremisesSamAccountName = getUsers("/graph/v1.0/users?$orderby=onPremisesSamAccountName%20desc")
-				Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
-				Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user1"))
-				Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user2"))
-
-				// Handles invalid sort field
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$orderby=invalid", nil)
-				svc.GetUsers(rr, r)
+		})
+	})
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+	DescribeTable("GetUsers handles unsupported or invalid filters",
+		func(filter string, status int) {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
+				Permission: &settingsmsg.Permission{
+					Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
+					Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+				},
+			}, nil)
+
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(status))
+		},
+		Entry("with invalid filter", "invalid", http.StatusBadRequest),
+		Entry("with unsupported filter for user property", "mail eq 'unsupported'", http.StatusNotImplemented),
+		Entry("with unsupported filter operation", "mail add 10", http.StatusNotImplemented),
+		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),
+		Entry("with unsupported filter operation ", "memberOf/any(n:n/id ne 1)", http.StatusNotImplemented),
+		Entry("with unsupported filter operand type", "memberOf/any(n:n/id eq 1)", http.StatusNotImplemented),
+		Entry("with unsupported memberOf lambda filter property", "memberOf/any(n:n/name eq 'name')", http.StatusNotImplemented),
+		Entry("with unsupported appRoleAssignments lambda filter property", "appRoleAssignments/any(n:n/id eq 'id')", http.StatusNotImplemented),
+		Entry("with unsupported appRoleAssignments lambda filter property",
+			"appRoleAssignments/any(n:n/id eq 'id') and appRoleAssignments/any(n:n/id eq 'id')", http.StatusNotImplemented),
+		Entry("with unsupported appRoleAssignments lambda filter operation",
+			"appRoleAssignments/all(n:n/appRoleId eq 'id') and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
+		Entry("with unsupported appRoleAssignments lambda filter operation",
+			"appRoleAssignments/any(n:n/appRoleId ne 'id') and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
+		Entry("with unsupported appRoleAssignments lambda filter operation",
+			"appRoleAssignments/any(n:n/appRoleId eq 1) and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
+	)
 
-			It("expands the appRoleAssignments", func() {
-
-				user := &libregraph.User{}
-				user.SetId("user1")
-				user.SetMail("z@example.com")
-				user.SetDisplayName("9")
-				user.SetOnPremisesSamAccountName("9")
-				user2 := &libregraph.User{}
-				user2.SetId("user2")
-				user2.SetMail("a@example.com")
-				user2.SetDisplayName("1")
-				user2.SetOnPremisesSamAccountName("1")
-				users := []*libregraph.User{user, user2}
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
-
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
-					},
+	DescribeTable("With a valid filter",
+		func(filter string, status int) {
+			permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
+				Permission: &settingsmsg.Permission{
+					Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
+					Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+				},
+			}, nil)
+
+			user := &libregraph.User{}
+			user.SetId("25cb7bc0-3168-4a0c-adbe-396f478ad494")
+			users := []*libregraph.User{user}
+			identityBackend.On("GetGroupMembers", mock.Anything, "25cb7bc0-3168-4a0c-adbe-396f478ad494", mock.Anything).Return(users, nil)
+			identityBackend.On("GetGroupMembers", mock.Anything, "2713f1d5-6822-42bd-ad56-9f6c55a3a8fa", mock.Anything).Return([]*libregraph.User{}, nil)
+			identityBackend.On("GetUsers", mock.Anything, mock.Anything).Return([]*libregraph.User{user}, nil)
+			roleService.On("ListRoleAssignmentsFiltered", mock.Anything, mock.Anything, mock.Anything).
+				Return(func(ctx context.Context, in *settings.ListRoleAssignmentsFilteredRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
+					return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
+						{
+							Id:          "some-appRoleAssignment-ID",
+							AccountUuid: user.GetId(),
+							RoleId:      "some-appRole-ID",
+						},
+					}}
 				}, nil)
-				roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
+			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).
+				Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
 					return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
 						{
 							Id:          "some-appRoleAssignment-ID",
-							AccountUuid: in.GetAccountUuid(),
+							AccountUuid: user.GetId(),
 							RoleId:      "some-appRole-ID",
 						},
 					}}
 				}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
+			svc.GetUsers(rr, r)
+
+			Expect(rr.Code).To(Equal(status))
+		},
+		Entry("with memberOf lambda filter with UUID", "memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494)", http.StatusOK),
+		Entry("with memberOf lambda filter with UUID string", "memberOf/any(n:n/id eq '25cb7bc0-3168-4a0c-adbe-396f478ad494')", http.StatusOK),
+		Entry("with appRoleAssignments lambda filter with appRoleId", "appRoleAssignments/any(n:n/appRoleId eq 'some-appRole-ID')", http.StatusOK),
+		Entry("with two memberOf lambda filters combined with and",
+			"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
+			http.StatusOK),
+		Entry("with two memberOf lambda filters combined with or",
+			"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) or memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
+			http.StatusOK),
+		Entry("with supported appRoleAssignments lambda filter property",
+			"appRoleAssignments/any(n:n/appRoleId eq 'some-appRoleAssignment-ID') and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
+			http.StatusOK),
+	)
 
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=appRoleAssignments", nil)
-				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.GetUsers(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusOK))
-
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
-
-				res := userList{}
-				err = json.Unmarshal(data, &res)
-				Expect(err).ToNot(HaveOccurred())
-
-				responseUsers := res.Value
-				Expect(len(responseUsers)).To(Equal(2))
-				Expect(responseUsers[0].GetId()).To(Equal("user1"))
-				Expect(responseUsers[0].GetAppRoleAssignments()).To(HaveLen(1))
-				Expect(responseUsers[0].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-				Expect(responseUsers[0].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-				Expect(responseUsers[0].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
-				Expect(responseUsers[0].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-
-				Expect(responseUsers[1].GetId()).To(Equal("user2"))
-				Expect(responseUsers[1].GetAppRoleAssignments()).To(HaveLen(1))
-				Expect(responseUsers[1].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-				Expect(responseUsers[1].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-				Expect(responseUsers[1].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user2"))
-				Expect(responseUsers[1].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
+	Describe("GetUser", func() {
+		It("handles missing userids", func() {
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
+			svc.GetUser(rr, r)
 
-			})
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
 		})
 
-		DescribeTable("GetUsers handles unsupported or invalid filters",
-			func(filter string, status int) {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
-					},
-				}, nil)
-
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
-				svc.GetUsers(rr, r)
-
-				Expect(rr.Code).To(Equal(status))
-			},
-			Entry("with invalid filter", "invalid", http.StatusBadRequest),
-			Entry("with unsupported filter for user property", "mail eq 'unsupported'", http.StatusNotImplemented),
-			Entry("with unsupported filter operation", "mail add 10", http.StatusNotImplemented),
-			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),
-			Entry("with unsupported filter operation ", "memberOf/any(n:n/id ne 1)", http.StatusNotImplemented),
-			Entry("with unsupported filter operand type", "memberOf/any(n:n/id eq 1)", http.StatusNotImplemented),
-			Entry("with unsupported memberOf lambda filter property", "memberOf/any(n:n/name eq 'name')", http.StatusNotImplemented),
-			Entry("with unsupported appRoleAssignments lambda filter property", "appRoleAssignments/any(n:n/id eq 'id')", http.StatusNotImplemented),
-			Entry("with unsupported appRoleAssignments lambda filter property",
-				"appRoleAssignments/any(n:n/id eq 'id') and appRoleAssignments/any(n:n/id eq 'id')", http.StatusNotImplemented),
-			Entry("with unsupported appRoleAssignments lambda filter operation",
-				"appRoleAssignments/all(n:n/appRoleId eq 'id') and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
-			Entry("with unsupported appRoleAssignments lambda filter operation",
-				"appRoleAssignments/any(n:n/appRoleId ne 'id') and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
-			Entry("with unsupported appRoleAssignments lambda filter operation",
-				"appRoleAssignments/any(n:n/appRoleId eq 1) and appRoleAssignments/any(n:n/appRoleId eq 'id')", http.StatusNotImplemented),
-		)
-
-		DescribeTable("With a valid filter",
-			func(filter string, status int) {
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
-					},
-				}, nil)
-
-				user := &libregraph.User{}
-				user.SetId("25cb7bc0-3168-4a0c-adbe-396f478ad494")
-				users := []*libregraph.User{user}
-				identityBackend.On("GetGroupMembers", mock.Anything, "25cb7bc0-3168-4a0c-adbe-396f478ad494", mock.Anything).Return(users, nil)
-				identityBackend.On("GetGroupMembers", mock.Anything, "2713f1d5-6822-42bd-ad56-9f6c55a3a8fa", mock.Anything).Return([]*libregraph.User{}, nil)
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything).Return([]*libregraph.User{user}, nil)
-				roleService.On("ListRoleAssignmentsFiltered", mock.Anything, mock.Anything, mock.Anything).
-					Return(func(ctx context.Context, in *settings.ListRoleAssignmentsFilteredRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
-						return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
-							{
-								Id:          "some-appRoleAssignment-ID",
-								AccountUuid: user.GetId(),
-								RoleId:      "some-appRole-ID",
-							},
-						}}
-					}, nil)
-				roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).
-					Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
-						return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
-							{
-								Id:          "some-appRoleAssignment-ID",
-								AccountUuid: user.GetId(),
-								RoleId:      "some-appRole-ID",
-							},
-						}}
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
-				svc.GetUsers(rr, r)
-
-				Expect(rr.Code).To(Equal(status))
-			},
-			Entry("with memberOf lambda filter with UUID", "memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494)", http.StatusOK),
-			Entry("with memberOf lambda filter with UUID string", "memberOf/any(n:n/id eq '25cb7bc0-3168-4a0c-adbe-396f478ad494')", http.StatusOK),
-			Entry("with appRoleAssignments lambda filter with appRoleId", "appRoleAssignments/any(n:n/appRoleId eq 'some-appRole-ID')", http.StatusOK),
-			Entry("with two memberOf lambda filters combined with and",
-				"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
-				http.StatusOK),
-			Entry("with two memberOf lambda filters combined with or",
-				"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) or memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
-				http.StatusOK),
-			Entry("with supported appRoleAssignments lambda filter property",
-				"appRoleAssignments/any(n:n/appRoleId eq 'some-appRoleAssignment-ID') and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
-				http.StatusOK),
-		)
-
-		Describe("GetUser", func() {
-			It("handles missing userids", func() {
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
-				svc.GetUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
-
-			It("gets the user", func() {
-				user := &libregraph.User{}
-				user.SetId("user1")
-
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+		It("gets the user", func() {
+			user := &libregraph.User{}
+			user.SetId("user1")
+
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", *user.Id)
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.GetUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-
-				Expect(err).ToNot(HaveOccurred())
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(responseUser.GetId()).To(Equal("user1"))
-				Expect(len(responseUser.GetDrives())).To(Equal(0))
-			})
-
-			It("includes the personal space if requested", func() {
-				user := &libregraph.User{}
-				user.SetId("user1")
-
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-				gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
-					Status:     status.NewOK(ctx),
-					TotalBytes: 10,
-				}, nil)
-				gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
-					Status: status.NewOK(ctx),
-					StorageSpaces: []*provider.StorageSpace{
-						{
-							Id:        &provider.StorageSpaceId{OpaqueId: "drive1"},
-							Root:      &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
-							SpaceType: "project",
-						},
-						{
-							Id:        &provider.StorageSpaceId{OpaqueId: "personal"},
-							Owner:     &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "user1"}},
-							Root:      &provider.ResourceId{SpaceId: "personal", OpaqueId: "personal"},
-							SpaceType: "personal",
-						},
 					},
 				}, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", *user.Id)
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.GetUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+
+			Expect(err).ToNot(HaveOccurred())
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(responseUser.GetId()).To(Equal("user1"))
+			Expect(len(responseUser.GetDrives())).To(Equal(0))
+		})
+
+		It("includes the personal space if requested", func() {
+			user := &libregraph.User{}
+			user.SetId("user1")
+
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+			gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
+				Status:     status.NewOK(ctx),
+				TotalBytes: 10,
+			}, nil)
+			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
+				Status: status.NewOK(ctx),
+				StorageSpaces: []*provider.StorageSpace{
+					{
+						Id:        &provider.StorageSpaceId{OpaqueId: "drive1"},
+						Root:      &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
+						SpaceType: "project",
+					},
+					{
+						Id:        &provider.StorageSpaceId{OpaqueId: "personal"},
+						Owner:     &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "user1"}},
+						Root:      &provider.ResourceId{SpaceId: "personal", OpaqueId: "personal"},
+						SpaceType: "personal",
+					},
+				},
+			}, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=drive", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", *user.Id)
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.GetUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(responseUser.GetId()).To(Equal("user1"))
-				Expect(*responseUser.GetDrive().Id).To(Equal("personal"))
-			})
-
-			It("includes the drives if requested", func() {
-				user := &libregraph.User{}
-				user.SetId("user1")
-
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-				gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
-					Status:     status.NewOK(ctx),
-					TotalBytes: 10,
-				}, nil)
-				gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
-					Status: status.NewOK(ctx),
-					StorageSpaces: []*provider.StorageSpace{
-						{
-							Id:   &provider.StorageSpaceId{OpaqueId: "drive1"},
-							Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
-						},
 					},
 				}, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=drive", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", *user.Id)
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.GetUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(responseUser.GetId()).To(Equal("user1"))
+			Expect(*responseUser.GetDrive().Id).To(Equal("personal"))
+		})
+
+		It("includes the drives if requested", func() {
+			user := &libregraph.User{}
+			user.SetId("user1")
+
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+			gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
+				Status:     status.NewOK(ctx),
+				TotalBytes: 10,
+			}, nil)
+			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
+				Status: status.NewOK(ctx),
+				StorageSpaces: []*provider.StorageSpace{
+					{
+						Id:   &provider.StorageSpaceId{OpaqueId: "drive1"},
+						Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
+					},
+				},
+			}, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=drives", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", *user.Id)
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.GetUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(responseUser.GetId()).To(Equal("user1"))
-				Expect(len(responseUser.GetDrives())).To(Equal(1))
-			})
-
-			It("expands the appRoleAssignments", func() {
-				user := &libregraph.User{}
-				user.SetId("user1")
-
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-
-				assignments := []*settingsmsg.UserRoleAssignment{
-					{
-						Id:          "some-appRoleAssignment-ID",
-						AccountUuid: "user1",
-						RoleId:      "some-appRole-ID",
 					},
-				}
-				roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-				valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
-					Return(&settings.GetValueResponse{
-						Value: &settingsmsg.ValueWithIdentifier{
-							Value: &settingsmsg.Value{
-								Value: &settingsmsg.Value_ListValue{
-									ListValue: &settingsmsg.ListValue{
-										Values: []*settingsmsg.ListOptionValue{
-											{
-												Option: &settingsmsg.ListOptionValue_StringValue{
-													StringValue: "it",
-												},
+				}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=drives", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", *user.Id)
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.GetUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(responseUser.GetId()).To(Equal("user1"))
+			Expect(len(responseUser.GetDrives())).To(Equal(1))
+		})
+
+		It("expands the appRoleAssignments", func() {
+			user := &libregraph.User{}
+			user.SetId("user1")
+
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+
+			assignments := []*settingsmsg.UserRoleAssignment{
+				{
+					Id:          "some-appRoleAssignment-ID",
+					AccountUuid: "user1",
+					RoleId:      "some-appRole-ID",
+				},
+			}
+			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
+			valueService.On("GetValueByUniqueIdentifiers", mock.Anything, mock.Anything, mock.Anything).
+				Return(&settings.GetValueResponse{
+					Value: &settingsmsg.ValueWithIdentifier{
+						Value: &settingsmsg.Value{
+							Value: &settingsmsg.Value_ListValue{
+								ListValue: &settingsmsg.ListValue{
+									Values: []*settingsmsg.ListOptionValue{
+										{
+											Option: &settingsmsg.ListOptionValue_StringValue{
+												StringValue: "it",
 											},
 										},
 									},
 								},
 							},
 						},
-					}, nil)
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1?$expand=appRoleAssignments", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.GetUser(rr, r)
+					},
+				}, nil)
+			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1?$expand=appRoleAssignments", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", user.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.GetUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			responseUser := &libregraph.User{}
+			err = json.Unmarshal(data, &responseUser)
+			Expect(err).ToNot(HaveOccurred())
+
+			Expect(responseUser.GetId()).To(Equal("user1"))
+			Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
+			Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
+		})
+	})
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
+	Describe("PostUser", func() {
+		var (
+			user *libregraph.User
 
-				data, err := io.ReadAll(rr.Body)
+			assertHandleBadAttributes = func(user *libregraph.User) {
+				userJson, err := json.Marshal(user)
 				Expect(err).ToNot(HaveOccurred())
 
-				responseUser := &libregraph.User{}
-				err = json.Unmarshal(data, &responseUser)
-				Expect(err).ToNot(HaveOccurred())
+				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+				svc.PostUser(rr, r)
 
-				Expect(responseUser.GetId()).To(Equal("user1"))
-				Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
-				Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-			})
+				Expect(rr.Code).To(Equal(http.StatusBadRequest))
+			}
+		)
+
+		BeforeEach(func() {
+			user = &libregraph.User{}
+			user.SetDisplayName("Display Name")
+			user.SetOnPremisesSamAccountName("user")
+			user.SetMail("user@example.com")
 		})
 
-		Describe("PostUser", func() {
-			var (
-				user *libregraph.User
+		It("handles invalid bodies", func() {
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", nil)
+			svc.PostUser(rr, r)
 
-				assertHandleBadAttributes = func(user *libregraph.User) {
-					userJson, err := json.Marshal(user)
-					Expect(err).ToNot(HaveOccurred())
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-					r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
-					svc.PostUser(rr, r)
+		It("handles missing display names", func() {
+			user.DisplayName = nil
+			assertHandleBadAttributes(user)
 
-					Expect(rr.Code).To(Equal(http.StatusBadRequest))
-				}
-			)
+		})
 
-			BeforeEach(func() {
-				user = &libregraph.User{}
-				user.SetDisplayName("Display Name")
-				user.SetOnPremisesSamAccountName("user")
-				user.SetMail("user@example.com")
-			})
+		It("handles missing OnPremisesSamAccountName", func() {
+			user.OnPremisesSamAccountName = nil
+			assertHandleBadAttributes(user)
 
-			It("handles invalid bodies", func() {
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", nil)
-				svc.PostUser(rr, r)
+			user.SetOnPremisesSamAccountName("")
+			assertHandleBadAttributes(user)
+		})
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+		It("handles bad Mails", func() {
+			user.SetMail("not-a-mail-address")
+			assertHandleBadAttributes(user)
+		})
 
-			It("handles missing display names", func() {
-				user.DisplayName = nil
-				assertHandleBadAttributes(user)
+		It("handles set Ids - they are read-only", func() {
+			user.SetId("/users/user")
+			assertHandleBadAttributes(user)
+		})
 
-			})
+		It("handles invalid userType", func() {
+			user.SetUserType("Clown")
+			assertHandleBadAttributes(user)
+		})
 
-			It("handles missing OnPremisesSamAccountName", func() {
-				user.OnPremisesSamAccountName = nil
-				assertHandleBadAttributes(user)
+		It("creates a user", func() {
+			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
+			identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
+				user.SetId("/users/user")
+				return &user
+			}, nil)
+			userJson, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
+
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.PostUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusCreated))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			createdUser := libregraph.User{}
+			err = json.Unmarshal(data, &createdUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(createdUser.GetUserType()).To(Equal("Member"))
+		})
 
-				user.SetOnPremisesSamAccountName("")
-				assertHandleBadAttributes(user)
-			})
+		It("creates a guest user", func() {
+			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
+			identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
+				user.SetId("/users/user")
+				return &user
+			}, nil)
 
-			It("handles bad Mails", func() {
-				user.SetMail("not-a-mail-address")
-				assertHandleBadAttributes(user)
-			})
+			user.SetUserType("Guest")
+			userJson, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
+
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.PostUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusCreated))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			createdUser := libregraph.User{}
+			err = json.Unmarshal(data, &createdUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(createdUser.GetUserType()).To(Equal("Guest"))
+		})
 
-			It("handles set Ids - they are read-only", func() {
+		It("creates a member user", func() {
+			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
+			identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
 				user.SetId("/users/user")
-				assertHandleBadAttributes(user)
-			})
+				return &user
+			}, nil)
 
-			It("handles invalid userType", func() {
-				user.SetUserType("Clown")
-				assertHandleBadAttributes(user)
-			})
+			user.SetUserType("Member")
+			userJson, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
 
-			It("creates a user", func() {
-				roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
-				identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
-					user.SetId("/users/user")
-					return &user
-				}, nil)
-				userJson, err := json.Marshal(user)
-				Expect(err).ToNot(HaveOccurred())
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
+			svc.PostUser(rr, r)
 
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
-				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.PostUser(rr, r)
+			Expect(rr.Code).To(Equal(http.StatusCreated))
+			data, err := io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
 
-				Expect(rr.Code).To(Equal(http.StatusCreated))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
+			createdUser := libregraph.User{}
+			err = json.Unmarshal(data, &createdUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(createdUser.GetUserType()).To(Equal("Member"))
+		})
 
-				createdUser := libregraph.User{}
-				err = json.Unmarshal(data, &createdUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(createdUser.GetUserType()).To(Equal("Member"))
-			})
+		Describe("Handling usernames with spaces", func() {
+			var (
+				newSvc = func(usernameMatch string) service.Service {
+					localCfg := defaults.FullDefaultConfig()
+					localCfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
+					localCfg.TokenManager.JWTSecret = "loremipsum"
+					localCfg.Commons = &shared.Commons{}
+					localCfg.GRPCClientTLS = &shared.GRPCClientTLS{}
+
+					localCfg.API.UsernameMatch = usernameMatch
+
+					localSvc, _ := service.NewService(
+						service.Config(localCfg),
+						service.WithGatewaySelector(gatewaySelector),
+						service.EventsPublisher(&eventsPublisher),
+						service.WithIdentityBackend(identityBackend),
+						service.WithRoleService(roleService),
+					)
+
+					return localSvc
+				}
+			)
 
-			It("creates a guest user", func() {
-				roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
-				identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
-					user.SetId("/users/user")
-					return &user
-				}, nil)
+			BeforeEach(func() {
+				user.SetOnPremisesSamAccountName("username with spaces")
+			})
 
-				user.SetUserType("Guest")
+			It("rejects a username with spaces if match regex is default", func() {
 				userJson, err := json.Marshal(user)
 				Expect(err).ToNot(HaveOccurred())
 
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
 				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.PostUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusCreated))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
 
-				createdUser := libregraph.User{}
-				err = json.Unmarshal(data, &createdUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(createdUser.GetUserType()).To(Equal("Guest"))
+				newSvc("default").PostUser(rr, r)
+				Expect(rr.Code).To(Equal(http.StatusBadRequest))
 			})
 
-			It("creates a member user", func() {
+			It("creates a user with spaces in username if regex is none", func() {
 				roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
 				identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
 					user.SetId("/users/user")
 					return &user
 				}, nil)
-
-				user.SetUserType("Member")
 				userJson, err := json.Marshal(user)
 				Expect(err).ToNot(HaveOccurred())
 
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(userJson))
+				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
 				r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-				svc.PostUser(rr, r)
+				newSvc("none").PostUser(rr, r)
 
 				Expect(rr.Code).To(Equal(http.StatusCreated))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
-
-				createdUser := libregraph.User{}
-				err = json.Unmarshal(data, &createdUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(createdUser.GetUserType()).To(Equal("Member"))
-			})
-
-			Describe("Handling usernames with spaces", func() {
-				var (
-					newSvc = func(usernameMatch string) service.Service {
-						localCfg := defaults.FullDefaultConfig()
-						localCfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
-						localCfg.TokenManager.JWTSecret = "loremipsum"
-						localCfg.Commons = &shared.Commons{}
-						localCfg.GRPCClientTLS = &shared.GRPCClientTLS{}
-
-						localCfg.API.UsernameMatch = usernameMatch
-
-						localSvc, _ := service.NewService(
-							service.Config(localCfg),
-							service.WithGatewaySelector(gatewaySelector),
-							service.EventsPublisher(&eventsPublisher),
-							service.WithIdentityBackend(identityBackend),
-							service.WithRoleService(roleService),
-						)
-
-						return localSvc
-					}
-				)
-
-				BeforeEach(func() {
-					user.SetOnPremisesSamAccountName("username with spaces")
-				})
-
-				It("rejects a username with spaces if match regex is default", func() {
-					userJson, err := json.Marshal(user)
-					Expect(err).ToNot(HaveOccurred())
-
-					r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
-					r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-
-					newSvc("default").PostUser(rr, r)
-					Expect(rr.Code).To(Equal(http.StatusBadRequest))
-				})
-
-				It("creates a user with spaces in username if regex is none", func() {
-					roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
-					identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
-						user.SetId("/users/user")
-						return &user
-					}, nil)
-					userJson, err := json.Marshal(user)
-					Expect(err).ToNot(HaveOccurred())
-
-					r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
-					r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-					newSvc("none").PostUser(rr, r)
-
-					Expect(rr.Code).To(Equal(http.StatusCreated))
-				})
 			})
-
 		})
 
-		Describe("DeleteUser", func() {
-			It("handles missing userids", func() {
-				r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
-				svc.DeleteUser(rr, r)
+	})
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+	Describe("DeleteUser", func() {
+		It("handles missing userids", func() {
+			r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
+			svc.DeleteUser(rr, r)
 
-			It("prevents a user from deleting themselves", func() {
-				lu := libregraph.User{}
-				lu.SetId(currentUser.Id.OpaqueId)
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-				r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", currentUser.Id.OpaqueId)
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.DeleteUser(rr, r)
+		It("prevents a user from deleting themselves", func() {
+			lu := libregraph.User{}
+			lu.SetId(currentUser.Id.OpaqueId)
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
 
-				Expect(rr.Code).To(Equal(http.StatusForbidden))
-			})
+			r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", currentUser.Id.OpaqueId)
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.DeleteUser(rr, r)
 
-			It("deletes a user from deleting themselves", func() {
-				otheruser := &userv1beta1.User{
-					Id: &userv1beta1.UserId{
-						OpaqueId: "otheruser",
-					},
-				}
+			Expect(rr.Code).To(Equal(http.StatusForbidden))
+		})
 
-				lu := libregraph.User{}
-				lu.SetId(otheruser.Id.OpaqueId)
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
-				identityBackend.On("DeleteUser", mock.Anything, mock.Anything).Return(nil)
-				gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{
-					Status: status.NewOK(ctx),
-				}, nil)
-				gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
-					Status: status.NewOK(ctx),
-					StorageSpaces: []*provider.StorageSpace{
-						{
-							Opaque:    &typesv1beta1.Opaque{},
-							Id:        &provider.StorageSpaceId{OpaqueId: "drive1"},
-							Root:      &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
-							SpaceType: "personal",
-							Owner:     otheruser,
-						},
+		It("deletes a user from deleting themselves", func() {
+			otheruser := &userv1beta1.User{
+				Id: &userv1beta1.UserId{
+					OpaqueId: "otheruser",
+				},
+			}
+
+			lu := libregraph.User{}
+			lu.SetId(otheruser.Id.OpaqueId)
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
+			identityBackend.On("DeleteUser", mock.Anything, mock.Anything).Return(nil)
+			gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{
+				Status: status.NewOK(ctx),
+			}, nil)
+			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
+				Status: status.NewOK(ctx),
+				StorageSpaces: []*provider.StorageSpace{
+					{
+						Opaque:    &typesv1beta1.Opaque{},
+						Id:        &provider.StorageSpaceId{OpaqueId: "drive1"},
+						Root:      &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
+						SpaceType: "personal",
+						Owner:     otheruser,
 					},
-				}, nil)
+				},
+			}, nil)
 
-				r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", lu.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.DeleteUser(rr, r)
+			r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/users/{userid}", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", lu.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.DeleteUser(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusNoContent))
-				gatewayClient.AssertNumberOfCalls(GinkgoT(), "DeleteStorageSpace", 2) // 2 calls for the home space. first trash, then purge
-			})
+			Expect(rr.Code).To(Equal(http.StatusNoContent))
+			gatewayClient.AssertNumberOfCalls(GinkgoT(), "DeleteStorageSpace", 2) // 2 calls for the home space. first trash, then purge
 		})
+	})
 
-		Describe("PatchUser", func() {
-			var (
-				user *libregraph.User
-			)
-
-			BeforeEach(func() {
-				user = &libregraph.User{}
-				user.SetDisplayName("Display Name")
-				user.SetOnPremisesSamAccountName("user")
-				user.SetMail("user@example.com")
-				user.SetId("/users/user")
-
-				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-			})
-
-			It("handles missing userids", func() {
-				r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/users/{userid}", nil)
-				svc.PatchUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
-
-			It("handles invalid bodies", func() {
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", nil)
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.PatchUser(rr, r)
-
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
-
-			It("handles invalid email", func() {
-				user.SetMail("invalid")
-				data, err := json.Marshal(user)
-				Expect(err).ToNot(HaveOccurred())
-
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.PatchUser(rr, r)
+	Describe("PatchUser", func() {
+		var (
+			user *libregraph.User
+		)
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+		BeforeEach(func() {
+			user = &libregraph.User{}
+			user.SetDisplayName("Display Name")
+			user.SetOnPremisesSamAccountName("user")
+			user.SetMail("user@example.com")
+			user.SetId("/users/user")
 
-			It("handles invalid userType", func() {
-				user.SetUserType("Clown")
-				data, err := json.Marshal(user)
-				Expect(err).ToNot(HaveOccurred())
+			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
+		})
 
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.PatchUser(rr, r)
+		It("handles missing userids", func() {
+			r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/users/{userid}", nil)
+			svc.PatchUser(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusBadRequest))
-			})
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-			It("updates attributes", func() {
-				user.SetUserType("Member")
-				identityBackend.On("UpdateUser", mock.Anything, user.GetId(), mock.Anything).Return(user, nil)
+		It("handles invalid bodies", func() {
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", nil)
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", user.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.PatchUser(rr, r)
 
-				user.SetUserType(("Member"))
-				user.SetDisplayName("New Display Name")
-				data, err := json.Marshal(user)
-				Expect(err).ToNot(HaveOccurred())
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(data))
-				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
-				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-				svc.PatchUser(rr, r)
+		It("handles invalid email", func() {
+			user.SetMail("invalid")
+			data, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err = io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", user.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.PatchUser(rr, r)
 
-				updatedUser := libregraph.User{}
-				err = json.Unmarshal(data, &updatedUser)
-				Expect(err).ToNot(HaveOccurred())
-				Expect(updatedUser.GetUserType()).To(Equal("Member"))
-				Expect(updatedUser.GetDisplayName()).To(Equal("New Display Name"))
-			})
-		})
-	})
-	When("OCM is enabled", func() {
-		BeforeEach(func() {
-			cfg.IncludeOCMSharees = true
-			svc, _ = service.NewService(
-				service.Config(cfg),
-				service.WithGatewaySelector(gatewaySelector),
-				service.EventsPublisher(&eventsPublisher),
-				service.WithIdentityBackend(identityBackend),
-				service.WithRoleService(roleService),
-				service.WithValueService(valueService),
-				service.PermissionService(permissionService),
-			)
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
 		})
-		Describe("GetUsers", func() {
-			It("lists the users", func() {
-				gatewayClient.On("FindAcceptedUsers", mock.Anything, mock.Anything).Return(&invitepb.FindAcceptedUsersResponse{
-					Status: status.NewOK(ctx),
-					AcceptedUsers: []*userv1beta1.User{
-						{
-							Username: "federated",
-							Id: &userv1beta1.UserId{
-								OpaqueId: "federated",
-								Type:     userv1beta1.UserType_USER_TYPE_FEDERATED,
-							},
-						},
-					},
-				}, nil)
-				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
-					Permission: &settingsmsg.Permission{
-						Operation:  settingsmsg.Permission_OPERATION_UNKNOWN,
-						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
-					},
-				}, nil)
 
-				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(
-					[]*libregraph.User{}, nil,
-				)
+		It("handles invalid userType", func() {
+			user.SetUserType("Clown")
+			data, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
 
-				r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
-				svc.GetUsers(rr, r)
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", user.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.PatchUser(rr, r)
 
-				Expect(rr.Code).To(Equal(http.StatusOK))
-				data, err := io.ReadAll(rr.Body)
-				Expect(err).ToNot(HaveOccurred())
-
-				res := userList{}
-				err = json.Unmarshal(data, &res)
-				Expect(err).ToNot(HaveOccurred())
+			Expect(rr.Code).To(Equal(http.StatusBadRequest))
+		})
 
-				Expect(len(res.Value)).To(Equal(1))
-				Expect(res.Value[0].GetId()).To(Equal("federated"))
-				Expect(res.Value[0].GetUserType()).To(Equal("Federated"))
-			})
+		It("updates attributes", func() {
+			user.SetUserType("Member")
+			identityBackend.On("UpdateUser", mock.Anything, user.GetId(), mock.Anything).Return(user, nil)
+
+			user.SetUserType(("Member"))
+			user.SetDisplayName("New Display Name")
+			data, err := json.Marshal(user)
+			Expect(err).ToNot(HaveOccurred())
+
+			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(data))
+			rctx := chi.NewRouteContext()
+			rctx.URLParams.Add("userID", user.GetId())
+			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
+			svc.PatchUser(rr, r)
+
+			Expect(rr.Code).To(Equal(http.StatusOK))
+			data, err = io.ReadAll(rr.Body)
+			Expect(err).ToNot(HaveOccurred())
+
+			updatedUser := libregraph.User{}
+			err = json.Unmarshal(data, &updatedUser)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(updatedUser.GetUserType()).To(Equal("Member"))
+			Expect(updatedUser.GetDisplayName()).To(Equal("New Display Name"))
 		})
 	})
 })