Repository: owncloud/ocis
Pull request #5769 (2023-03-08 12:33:13)

[test-only] add api tests to editing user name
created tests to editing user name: 

- [x] valid name
- [x] empty name
- [x] existing name
- [x] name with characters
- [x] name of disabled user
- [x] name of deleted user

- [x] deleted tests from expected failures with empty display name and email because was an error in the test implementation
- [x] collect existing tests to Scenario Outline

created bugs:
- #5755 
- #5763 


Patch:
diff --git a/.drone.env b/.drone.env
index 2605e618a00..d5b38f97617 100644
--- a/.drone.env
+++ b/.drone.env
@@ -1,3 +1,3 @@
 # The test runner source for UI tests
-WEB_COMMITID=c2f5d649e364bb2a52c53c3f81390726af296d7d
+WEB_COMMITID=62100dc92495cdacc7ef01db50998bd0912d40c3
 WEB_BRANCH=master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d41d47b8789..93c139a7bf4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ The following sections list the changes for unreleased.
 * Bugfix - Fix Search tag indexing: [#5405](https://github.com/owncloud/ocis/pull/5405)
 * Bugfix - Populate expanded properties: [#5421](https://github.com/owncloud/ocis/pull/5421)
 * Bugfix - Add portrait thumbnail resolutions: [#5656](https://github.com/owncloud/ocis/pull/5656)
+* Change - Bump libregraph lico: [#5768](https://github.com/owncloud/ocis/pull/5768)
 * Change - Remove the settings ui: [#5463](https://github.com/owncloud/ocis/pull/5463)
 * Enhancement - Add global env variable extractor: [#5164](https://github.com/owncloud/ocis/pull/5164)
 * Enhancement - Async Postprocessing: [#5207](https://github.com/owncloud/ocis/pull/5207)
@@ -33,11 +34,13 @@ The following sections list the changes for unreleased.
 * Enhancement - Make graph/education API errors more consistent: [#5682](https://github.com/owncloud/ocis/pull/5682)
 * Enhancement - Add endpoint to list permissions: [#5594](https://github.com/owncloud/ocis/pull/5594)
 * Enhancement - Better config for postprocessing service: [#5457](https://github.com/owncloud/ocis/pull/5457)
+* Enhancement - Add config option to enforce passwords on public links: [#5785](https://github.com/owncloud/ocis/pull/5785)
 * Enhancement - Add new permission for public links: [#5690](https://github.com/owncloud/ocis/pull/5690)
 * Enhancement - Added option to configure default quota per role: [#5616](https://github.com/owncloud/ocis/pull/5616)
 * Enhancement - Add expiration to user and group shares: [#5389](https://github.com/owncloud/ocis/pull/5389)
 * Enhancement - Space Management permissions: [#5441](https://github.com/owncloud/ocis/pull/5441)
 * Enhancement - Cli to purge expired trash-bin items: [#5500](https://github.com/owncloud/ocis/pull/5500)
+* Enhancement - Userlog: [#5699](https://github.com/owncloud/ocis/pull/5699)
 * Enhancement - Userlog Service: [#5610](https://github.com/owncloud/ocis/pull/5610)
 
 # Changelog for [2.0.0] (2022-11-30)
diff --git a/Dockerfile b/Dockerfile
index 3777d7207ad..3f007f9c504 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ COPY ./ /ocis/
 WORKDIR /ocis/ocis
 RUN make ci-node-generate
 
-FROM owncloudci/golang:1.18 as build
+FROM owncloudci/golang:1.19 as build
 
 COPY --from=generate /ocis /ocis
 
diff --git a/changelog/unreleased/bump-lico.md b/changelog/unreleased/bump-lico.md
new file mode 100644
index 00000000000..377be65f8c6
--- /dev/null
+++ b/changelog/unreleased/bump-lico.md
@@ -0,0 +1,6 @@
+Change: Bump libregraph lico
+
+We updated lico to the latest version
+* Update to 0.59.4 - upstream dropped the kc and cookie backends
+
+https://github.com/owncloud/ocis/pull/5768
diff --git a/changelog/unreleased/public-link-password.md b/changelog/unreleased/public-link-password.md
new file mode 100644
index 00000000000..fddffa3df61
--- /dev/null
+++ b/changelog/unreleased/public-link-password.md
@@ -0,0 +1,10 @@
+Enhancement: Add config option to enforce passwords on public links
+
+Added a new config option to enforce passwords on public links with "Uploader, Editor, Contributor" roles.
+
+The new options are:
+`OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD`, `SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD` and `FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD`.
+Check the docs on how to properly set them.
+
+https://github.com/owncloud/ocis/pull/5785
+https://github.com/owncloud/ocis/pull/5720
diff --git a/changelog/unreleased/userlog-improvements.md b/changelog/unreleased/userlog-improvements.md
new file mode 100644
index 00000000000..95e4e94bc54
--- /dev/null
+++ b/changelog/unreleased/userlog-improvements.md
@@ -0,0 +1,5 @@
+Enhancement: Userlog
+
+Enhane userlog service with proper api and messages
+
+https://github.com/owncloud/ocis/pull/5699
diff --git a/deployments/examples/ocis_wopi/config/onlyoffice/entrypoint-override.sh b/deployments/examples/ocis_wopi/config/onlyoffice/entrypoint-override.sh
new file mode 100755
index 00000000000..60179dfe8ee
--- /dev/null
+++ b/deployments/examples/ocis_wopi/config/onlyoffice/entrypoint-override.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -e
+
+# we can't mount it directly because the run-document-server.sh script wants to move it
+cp /etc/onlyoffice/documentserver/local.dist.json /etc/onlyoffice/documentserver/local.json
+
+/app/ds/run-document-server.sh
diff --git a/deployments/examples/ocis_wopi/config/onlyoffice/local.json b/deployments/examples/ocis_wopi/config/onlyoffice/local.json
new file mode 100755
index 00000000000..0db571fad42
--- /dev/null
+++ b/deployments/examples/ocis_wopi/config/onlyoffice/local.json
@@ -0,0 +1,71 @@
+{
+  "services": {
+    "CoAuthoring": {
+      "sql": {
+        "type": "postgres",
+        "dbHost": "localhost",
+        "dbPort": "5432",
+        "dbName": "onlyoffice",
+        "dbUser": "onlyoffice",
+        "dbPass": "onlyoffice"
+      },
+      "token": {
+        "enable": {
+          "request": {
+            "inbox": true,
+            "outbox": true
+          },
+          "browser": true
+        },
+        "inbox": {
+          "header": "Authorization"
+        },
+        "outbox": {
+          "header": "Authorization"
+        }
+      },
+      "secret": {
+        "inbox": {
+          "string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
+        },
+        "outbox": {
+          "string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
+        },
+        "session": {
+          "string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
+        }
+      }
+    }
+  },
+  "rabbitmq": {
+    "url": "amqp://guest:guest@localhost"
+  },
+  "FileConverter": {
+		"converter": {
+			"inputLimits": [
+				{
+				"type": "docx;dotx;docm;dotm",
+				"zip": {
+					"uncompressed": "1GB",
+					"template": "*.xml"
+				}
+				},
+				{
+				"type": "xlsx;xltx;xlsm;xltm",
+				"zip": {
+					"uncompressed": "1GB",
+					"template": "*.xml"
+				}
+				},
+				{
+				"type": "pptx;ppsx;potx;pptm;ppsm;potm",
+				"zip": {
+					"uncompressed": "1GB",
+					"template": "*.xml"
+				}
+				}
+			]
+		}
+	}
+
+}
diff --git a/deployments/examples/ocis_wopi/docker-compose.yml b/deployments/examples/ocis_wopi/docker-compose.yml
index d3f3c285ad6..d14c1f8bef4 100644
--- a/deployments/examples/ocis_wopi/docker-compose.yml
+++ b/deployments/examples/ocis_wopi/docker-compose.yml
@@ -199,9 +199,15 @@ services:
     image: onlyoffice/documentserver:7.3.0
     networks:
       ocis-net:
+    entrypoint:
+      - /bin/sh
+      - /entrypoint-override.sh
     environment:
       WOPI_ENABLED: "true"
       USE_UNAUTHORIZED_STORAGE: "${INSECURE:-false}" # self signed certificates
+    volumes:
+      - ./config/onlyoffice/entrypoint-override.sh:/entrypoint-override.sh
+      - ./config/onlyoffice/local.json:/etc/onlyoffice/documentserver/local.dist.json
     labels:
       - "traefik.enable=true"
       - "traefik.http.routers.onlyoffice.entrypoints=https"
@@ -209,7 +215,7 @@ services:
       - "traefik.http.routers.onlyoffice.tls.certresolver=http"
       - "traefik.http.routers.onlyoffice.service=onlyoffice"
       - "traefik.http.services.onlyoffice.loadbalancer.server.port=80"
-      # websockets can't be opend when this is ommitted
+      # websockets can't be opened when this is ommitted
       - "traefik.http.middlewares.onlyoffice.headers.customrequestheaders.X-Forwarded-Proto=https"
       - "traefik.http.routers.onlyoffice.middlewares=onlyoffice"
     logging:
diff --git a/docs/ocis/development/getting-started.md b/docs/ocis/development/getting-started.md
index 462cf4a8ae1..61cafc6de77 100644
--- a/docs/ocis/development/getting-started.md
+++ b/docs/ocis/development/getting-started.md
@@ -16,7 +16,7 @@ So we are trying to reflect this in the tooling. It should be kept simple and qu
 
 Besides standard development tools like git and a text editor, you need the following software for development:
 
-- Go >= v1.18 ([install instructions](https://golang.org/doc/install))
+- Go >= v1.19 ([install instructions](https://golang.org/doc/install))
 - pnpm ([install instructions](https://pnpm.io/installation))
 - docker ([install instructions](https://docs.docker.com/get-docker/))
 - docker-compose ([install instructions](https://docs.docker.com/compose/install/))
diff --git a/go.mod b/go.mod
index 640c548cf9c..16327e6af6b 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/blevesearch/bleve/v2 v2.3.6
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965
-	github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197
+	github.com/cs3org/reva/v2 v2.12.1-0.20230309145911-3ddca079da2c
 	github.com/disintegration/imaging v1.6.2
 	github.com/ggwhite/go-masker v1.0.9
 	github.com/go-chi/chi/v5 v5.0.7
@@ -48,7 +48,7 @@ require (
 	github.com/jellydator/ttlcache/v3 v3.0.1
 	github.com/justinas/alice v1.2.0
 	github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5
-	github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63
+	github.com/libregraph/lico v0.59.4
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
 	github.com/nats-io/nats-server/v2 v2.9.4
@@ -59,7 +59,7 @@ require (
 	github.com/onsi/ginkgo/v2 v2.7.0
 	github.com/onsi/gomega v1.25.0
 	github.com/orcaman/concurrent-map v1.0.0
-	github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc
+	github.com/owncloud/libre-graph-api-go v1.0.2-0.20230309112802-ff71ba8c90aa
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/xattr v0.4.9
 	github.com/prometheus/client_golang v1.14.0
@@ -78,8 +78,8 @@ require (
 	go.etcd.io/etcd/client/v3 v3.5.6
 	go.opencensus.io v0.23.0
 	go.opentelemetry.io/otel v1.14.0
-	go.opentelemetry.io/otel/exporters/jaeger v1.11.2
-	go.opentelemetry.io/otel/sdk v1.13.0
+	go.opentelemetry.io/otel/exporters/jaeger v1.14.0
+	go.opentelemetry.io/otel/sdk v1.14.0
 	go.opentelemetry.io/otel/trace v1.14.0
 	golang.org/x/crypto v0.7.0
 	golang.org/x/exp v0.0.0-20221026004748-78e5e7837ae6
@@ -145,7 +145,7 @@ require (
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/crewjam/httperr v0.2.0 // indirect
-	github.com/crewjam/saml v0.4.9 // indirect
+	github.com/crewjam/saml v0.4.10 // indirect
 	github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/deckarep/golang-set v1.8.0 // indirect
@@ -156,12 +156,11 @@ require (
 	github.com/dustin/go-humanize v1.0.0 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/emvi/iso-639-1 v1.0.1 // indirect
-	github.com/eternnoir/gncp v0.0.0-20170707042257-c70df2d0cd68 // indirect
 	github.com/evanphx/json-patch/v5 v5.5.0 // indirect
 	github.com/fatih/color v1.13.0 // indirect
 	github.com/felixge/httpsnoop v1.0.1 // indirect
 	github.com/fsnotify/fsnotify v1.5.4 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.1 // indirect
 	github.com/gdexlab/go-render v1.0.1 // indirect
 	github.com/go-acme/lego/v4 v4.4.0 // indirect
 	github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
@@ -215,6 +214,7 @@ require (
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.11 // indirect
 	github.com/klauspost/cpuid/v2 v2.1.0 // indirect
+	github.com/libregraph/oidc-go v1.0.0 // indirect
 	github.com/longsleep/go-metrics v1.0.0 // indirect
 	github.com/longsleep/rndm v1.2.0 // indirect
 	github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
@@ -289,7 +289,7 @@ require (
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	stash.kopano.io/kgol/kcc-go/v5 v5.0.1 // indirect
+	sigs.k8s.io/yaml v1.3.0 // indirect
 )
 
 replace github.com/cs3org/go-cs3apis => github.com/c0rby/go-cs3apis v0.0.0-20230110100311-5b424f1baa35
diff --git a/go.sum b/go.sum
index f50bb865e86..6760c353a09 100644
--- a/go.sum
+++ b/go.sum
@@ -143,7 +143,6 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
 github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
 github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
 github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -336,24 +335,23 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
 github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
 github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
-github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A=
-github.com/crewjam/saml v0.4.9 h1:X2jDv4dv3IvfT9t+RhADavzNFAcq3fVxzTCIH3G605U=
-github.com/crewjam/saml v0.4.9/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4=
+github.com/crewjam/saml v0.4.10 h1:Rjs6x4s/aQFXiaPjw3uhB4VdxRqoxHXOJrrj4BsMn9o=
+github.com/crewjam/saml v0.4.10/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4=
 github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197 h1:CGUH/W7ZNN3pm7ElRq3vYfVIkoHj3975EY7veaR/nyY=
 github.com/cs3org/reva/v2 v2.12.1-0.20230308104654-4ebb727fa197/go.mod h1:EGPAVHVqMHZ6GBo4T6QMw13D0djGU8ZCi/o5siJ9IUs=
+github.com/cs3org/reva/v2 v2.12.1-0.20230309145911-3ddca079da2c h1:uCeS2jKpvnarxRrnbawVyD8OPvplecRHd8oJusoOxq0=
+github.com/cs3org/reva/v2 v2.12.1-0.20230309145911-3ddca079da2c/go.mod h1:EGPAVHVqMHZ6GBo4T6QMw13D0djGU8ZCi/o5siJ9IUs=
 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
 github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
 github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
 github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
 github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
@@ -395,8 +393,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/eternnoir/gncp v0.0.0-20170707042257-c70df2d0cd68 h1:DHBMBKJK69xBWnD/jNkTN0sOT7nT7I5If9VMsk9Jj5Y=
-github.com/eternnoir/gncp v0.0.0-20170707042257-c70df2d0cd68/go.mod h1:8FuQ7lU9ZvIJGvc04F/qblkjqIfBahAoEFV+XPxByGw=
 github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc=
 github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
@@ -414,8 +410,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
-github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
-github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
+github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q=
+github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
 github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
 github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM=
 github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
@@ -428,8 +424,6 @@ github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc
 github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0=
 github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
 github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
@@ -460,7 +454,6 @@ github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
 github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
 github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
-github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
 github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
 github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
 github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o=
@@ -589,8 +582,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
-github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@@ -899,14 +890,15 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 h1:brLMXSjWoWhGXs8LpK+Lx+FQCtGLUa51Mq/ggHv9AV0=
 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5/go.mod h1:Tnm4pyVJTEbHm3GUNmceWT1DHzdrYqrJmZyt/xh7L+A=
-github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63 h1:oPqyRePmq+59YF1tAur7WXuM/z/epRd+HGGyPPx2Vv8=
-github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63/go.mod h1:KZ4X+bEbOQMSV6iPysZEqVO/Pa5Mvo7xhhcLwUNPjmw=
+github.com/libregraph/lico v0.59.4 h1:aQNwWAtC3hyQeZpP+5U1M7PIrPtvHanRWRfMp33wbUQ=
+github.com/libregraph/lico v0.59.4/go.mod h1:IgqScTns+CPYVjNuKhNFk5rfy7NsZP83FbuPkFZcTdg=
+github.com/libregraph/oidc-go v1.0.0 h1:l2tE/EwLyLXVy0B5BuVKgIFX9pNpz/5J3x5IBw0KEhc=
+github.com/libregraph/oidc-go v1.0.0/go.mod h1:7TRHrY/H1Vg6JqFjV0oAe1+kN+mGFBqXYvclSyvhRyc=
 github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=
 github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
 github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
 github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
 github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc=
-github.com/longsleep/go-metrics v0.0.0-20170706183227-c1943bcf9047/go.mod h1:Eq9KjddJTZCHG0ja+SEJNp739Um4URrcBuccq3Ih/NI=
 github.com/longsleep/go-metrics v1.0.0 h1:o2A6Dbu4MhLpZuL444WFoZzM7X7igewrj2MouwTlmVM=
 github.com/longsleep/go-metrics v1.0.0/go.mod h1:w6QO1LBkVla70FZrrF6XcB0YN+jTEYugjkn3+6RYTSM=
 github.com/longsleep/rndm v1.2.0 h1:wPl+kIMyIUTUFW5+2b327DmM1Rlj+gmexsiyOTB7rzM=
@@ -1044,7 +1036,6 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
@@ -1064,6 +1055,8 @@ github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CF
 github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
 github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc h1:FS5FC8kLv1RSEOKkZJ0Dxr1QJtv0Q18FaPXSKUsVnAc=
 github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc/go.mod h1:iKdVH6nYpI8RBeK9sjeLfzrPByST6r9d+NG2IJHoJmU=
+github.com/owncloud/libre-graph-api-go v1.0.2-0.20230309112802-ff71ba8c90aa h1:Lab1HHZ7Z8cBjojLDSfGYbGSZT08iww25uTW+EV1Sao=
+github.com/owncloud/libre-graph-api-go v1.0.2-0.20230309112802-ff71ba8c90aa/go.mod h1:iKdVH6nYpI8RBeK9sjeLfzrPByST6r9d+NG2IJHoJmU=
 github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
 github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -1193,14 +1186,12 @@ github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJ
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
-github.com/sirupsen/logrus v1.0.3/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
@@ -1220,15 +1211,12 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
 github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
-github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
 github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -1314,7 +1302,6 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
 github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
 github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
-github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
 go-micro.dev/v4 v4.9.0 h1:pd1CpqMT9hA47jSmX8mfdGK865PkMh95Rwj5RdfqPqE=
 go-micro.dev/v4 v4.9.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -1343,10 +1330,10 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.3
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4/go.mod h1:05eWWy6ZWzmpeImD3UowLTB3VjDMU1yxQ+ENuVWDM3c=
 go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
 go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
-go.opentelemetry.io/otel/exporters/jaeger v1.11.2 h1:ES8/j2+aB+3/BUw51ioxa50V9btN1eew/2J7N7n1tsE=
-go.opentelemetry.io/otel/exporters/jaeger v1.11.2/go.mod h1:nwcF/DK4Hk0auZ/a5vw20uMsaJSXbzeeimhN5f9d0Lc=
-go.opentelemetry.io/otel/sdk v1.13.0 h1:BHib5g8MvdqS65yo2vV1s6Le42Hm6rrw08qU6yz5JaM=
-go.opentelemetry.io/otel/sdk v1.13.0/go.mod h1:YLKPx5+6Vx/o1TCUYYs+bpymtkmazOMT6zoRrC7AQ7I=
+go.opentelemetry.io/otel/exporters/jaeger v1.14.0 h1:CjbUNd4iN2hHmWekmOqZ+zSCU+dzZppG8XsV+A3oc8Q=
+go.opentelemetry.io/otel/exporters/jaeger v1.14.0/go.mod h1:4Ay9kk5vELRrbg5z4cpP9EtmQRFap2Wb0woPG4lujZA=
+go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
+go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
 go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
 go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -1367,7 +1354,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
 go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
-golang.org/x/crypto v0.0.0-20170711145318-dd85ac7e6a88/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1384,7 +1370,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -1398,7 +1383,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -1499,7 +1483,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1704,7 +1687,6 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
 golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1999,7 +1981,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/Acconut/lockfile.v1 v1.1.0/go.mod h1:6UCz3wJ8tSFUsPR6uP/j8uegEtDuEEqFxlpi0JI4Umw=
-gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2009,7 +1990,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
 gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
 gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -2063,12 +2043,9 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-stash.kopano.io/kgol/kcc-go/v5 v5.0.1 h1:urR9hOR6TnTKjGkzZKac/a9cA8ws1WecWLTgiYubLQw=
-stash.kopano.io/kgol/kcc-go/v5 v5.0.1/go.mod h1:0ZmjWapy3zp+TAjZI6iCrcfh+BthZbB2WM1VfhDgNB4=
-stash.kopano.io/kgol/ksurveyclient-go v0.6.1/go.mod h1:LJMDQBROS2oXxBN04eSI6j1KhgWlqMFd8xKjXV4Irtw=
-stash.kopano.io/kgol/oidc-go v0.3.2/go.mod h1:roVKz8FVmPcdL4pUu+Gzk+GH2kOhz1UvnuMNTkjHyH8=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 stash.kopano.io/kgol/oidc-go v0.3.4 h1:pWANXF8gCPn6w+bowuWrOtmUueCyS70xLd4I8DbnIYs=
 stash.kopano.io/kgol/oidc-go v0.3.4/go.mod h1:roVKz8FVmPcdL4pUu+Gzk+GH2kOhz1UvnuMNTkjHyH8=
-stash.kopano.io/kgol/rndm v1.1.1/go.mod h1:CBvpAHlOwyu/XipxfLGk02UN3K3P6hQ8E2JoTbNWfJU=
 stash.kopano.io/kgol/rndm v1.1.2 h1:vriNehb5NuglfGqZPkgeFr2Y5AjXtQCF4vEl4kqc6nc=
 stash.kopano.io/kgol/rndm v1.1.2/go.mod h1:CBvpAHlOwyu/XipxfLGk02UN3K3P6hQ8E2JoTbNWfJU=
diff --git a/ocis-pkg/version/version.go b/ocis-pkg/version/version.go
index 36a0316fdff..e27422fda06 100644
--- a/ocis-pkg/version/version.go
+++ b/ocis-pkg/version/version.go
@@ -16,7 +16,7 @@ var (
 	// LatestTag is the latest released version plus the dev meta version.
 	// Will be overwritten by the release pipeline
 	// Needs a manual change for every tagged release
-	LatestTag = "2.0.0+dev"
+	LatestTag = "3.0.0-alpha.1+dev"
 
 	// Date indicates the build date.
 	Date = time.Now().Format("20060102")
diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go
index e0ee57901dd..ba68cb2004c 100644
--- a/services/frontend/pkg/config/config.go
+++ b/services/frontend/pkg/config/config.go
@@ -118,16 +118,17 @@ type DataGateway struct {
 }
 
 type OCS struct {
-	Prefix                  string             `yaml:"prefix" env:"FRONTEND_OCS_PREFIX" desc:"Path prefix for the OCS service"`
-	SharePrefix             string             `yaml:"share_prefix" env:"FRONTEND_OCS_SHARE_PREFIX" desc:"Path prefix for shares."`
-	HomeNamespace           string             `yaml:"home_namespace" env:"FRONTEND_OCS_HOME_NAMESPACE" desc:"Homespace namespace identifier."`
-	AdditionalInfoAttribute string             `yaml:"additional_info_attribute" env:"FRONTEND_OCS_ADDITIONAL_INFO_ATTRIBUTE" desc:"Additional information attribute for the user like {{.Mail}}."`
-	ResourceInfoCacheTTL    int                `yaml:"resource_info_cache_ttl" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_TTL" desc:"Max TTL in seconds for the resource info cache. 0 disables the cache."`
-	ResourceInfoCacheType   string             `yaml:"resource_info_cache_type" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_TYPE" desc:"Resource info cache type. Supported values are 'memory' and 'redis'."`
-	ResourceInfoCaches      ResourceInfoCaches `yaml:"resource_info_caches,omitempty"` // only used for redis
-	CacheWarmupDriver       string             `yaml:"cache_warmup_driver,omitempty"`  // not supported by the oCIS product, therefore not part of docs
-	CacheWarmupDrivers      CacheWarmupDrivers `yaml:"cache_warmup_drivers,omitempty"` // not supported by the oCIS product, therefore not part of docs
-	EnableDenials           bool               `yaml:"enable_denials" env:"FRONTEND_OCS_ENABLE_DENIALS" desc:"EXPERIMENTAL: enable the feature to deny access on folders."`
+	Prefix                               string             `yaml:"prefix" env:"FRONTEND_OCS_PREFIX" desc:"URL prefix for the OCS service. Note that the string must not start with '/'."`
+	SharePrefix                          string             `yaml:"share_prefix" env:"FRONTEND_OCS_SHARE_PREFIX" desc:"Path prefix for shares as part of an ocis resource. Note that the path must start with '/'."`
+	HomeNamespace                        string             `yaml:"home_namespace" env:"FRONTEND_OCS_HOME_NAMESPACE" desc:"Homespace namespace identifier."`
+	AdditionalInfoAttribute              string             `yaml:"additional_info_attribute" env:"FRONTEND_OCS_ADDITIONAL_INFO_ATTRIBUTE" desc:"Additional information attribute for the user like {{.Mail}}."`
+	ResourceInfoCacheTTL                 int                `yaml:"resource_info_cache_ttl" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_TTL" desc:"Max TTL in seconds for the resource info cache. 0 disables the cache."`
+	ResourceInfoCacheType                string             `yaml:"resource_info_cache_type" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_TYPE" desc:"The type of the resource info cache. Supported values are 'memory' and 'redis'."`
+	ResourceInfoCaches                   ResourceInfoCaches `yaml:"resource_info_caches,omitempty"` // only used for redis
+	CacheWarmupDriver                    string             `yaml:"cache_warmup_driver,omitempty"`  // not supported by the oCIS product, therefore not part of docs
+	CacheWarmupDrivers                   CacheWarmupDrivers `yaml:"cache_warmup_drivers,omitempty"` // not supported by the oCIS product, therefore not part of docs
+	EnableDenials                        bool               `yaml:"enable_denials" env:"FRONTEND_OCS_ENABLE_DENIALS" desc:"EXPERIMENTAL: enable the feature to deny access on folders."`
+	WriteablePublicShareMustHavePassword bool               `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on Uploader, Editor or Contributor shares."`
 }
 
 // ResourceInfoCaches holds resource info cache configurations
@@ -137,9 +138,9 @@ type ResourceInfoCaches struct {
 
 // RedisDriver holds redis configuration
 type RedisDriver struct {
-	Address  string `yaml:"address" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_ADDR" desc:"Redis service address"`
-	Username string `yaml:"username" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_USERNAME" desc:"Redis username"`
-	Password string `yaml:"password" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_PASSWORD" desc:"Redis password"`
+	Address  string `yaml:"address" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_ADDR" desc:"A comma separated list of addresses to access the configured store. This has no effect when the 'memory' store is configured. Note that the behaviour how addresses are used is dependent on the library of the configured store."`
+	Username string `yaml:"username" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_USERNAME" desc:"The username to access the redis cache."`
+	Password string `yaml:"password" env:"FRONTEND_OCS_RESOURCE_INFO_CACHE_REDIS_PASSWORD" desc:"The password to access the redis cache."`
 }
 
 type CacheWarmupDrivers struct {
diff --git a/services/frontend/pkg/revaconfig/config.go b/services/frontend/pkg/revaconfig/config.go
index 4fe63418fc2..70da96c8e4b 100644
--- a/services/frontend/pkg/revaconfig/config.go
+++ b/services/frontend/pkg/revaconfig/config.go
@@ -229,8 +229,8 @@ func FrontendConfigFromStruct(cfg *config.Config) (map[string]interface{}, error
 										"enforced": false,
 										"enforced_for": map[string]interface{}{
 											"read_only":   false,
-											"read_write":  false,
-											"upload_only": false,
+											"read_write":  cfg.OCS.WriteablePublicShareMustHavePassword,
+											"upload_only": cfg.OCS.WriteablePublicShareMustHavePassword,
 										},
 									},
 									"expire_date": map[string]interface{}{
@@ -267,6 +267,9 @@ func FrontendConfigFromStruct(cfg *config.Config) (map[string]interface{}, error
 								"share_jail": cfg.EnableShareJail,
 								"max_quota":  cfg.MaxQuota,
 							},
+							"notifications": map[string]interface{}{
+								"endpoints": []string{"list", "get", "delete"},
+							},
 						},
 						"version": map[string]interface{}{
 							"product":        "Infinite Scale",
diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go
index 7d1d19e5f27..5266e4f06e7 100644
--- a/services/graph/pkg/identity/ldap.go
+++ b/services/graph/pkg/identity/ldap.go
@@ -258,13 +258,13 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.
 	var updateNeeded bool
 
 	// Don't allow updates of the ID
-	if user.Id != nil && *user.Id != "" {
-		if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != *user.Id {
+	if user.GetId() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != user.GetId() {
 			return nil, errorcode.New(errorcode.NotAllowed, "changing the UserId is not allowed")
 		}
 	}
-	if user.OnPremisesSamAccountName != nil && *user.OnPremisesSamAccountName != "" {
-		if eu := e.GetEqualFoldAttributeValue(i.userAttributeMap.userName); eu != *user.OnPremisesSamAccountName {
+	if user.GetOnPremisesSamAccountName() != "" {
+		if eu := e.GetEqualFoldAttributeValue(i.userAttributeMap.userName); eu != user.GetOnPremisesSamAccountName() {
 			e, err = i.changeUserName(ctx, e.DN, eu, user.GetOnPremisesSamAccountName())
 			if err != nil {
 				return nil, err
@@ -273,19 +273,31 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.
 	}
 
 	mr := ldap.ModifyRequest{DN: e.DN}
-	if user.DisplayName != nil && *user.DisplayName != "" {
-		if e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName) != *user.DisplayName {
-			mr.Replace(i.userAttributeMap.displayName, []string{*user.DisplayName})
+	if user.GetDisplayName() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName) != user.GetDisplayName() {
+			mr.Replace(i.userAttributeMap.displayName, []string{user.GetDisplayName()})
 			updateNeeded = true
 		}
 	}
-	if user.Mail != nil && *user.Mail != "" {
-		if e.GetEqualFoldAttributeValue(i.userAttributeMap.mail) != *user.Mail {
-			mr.Replace(i.userAttributeMap.mail, []string{*user.Mail})
+	if user.GetMail() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.mail) != user.GetMail() {
+			mr.Replace(i.userAttributeMap.mail, []string{user.GetMail()})
 			updateNeeded = true
 		}
 	}
-	if user.PasswordProfile != nil && user.PasswordProfile.Password != nil && *user.PasswordProfile.Password != "" {
+	if user.GetSurname() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.surname) != user.GetSurname() {
+			mr.Replace(i.userAttributeMap.surname, []string{user.GetSurname()})
+			updateNeeded = true
+		}
+	}
+	if user.GetGivenName() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName) != user.GetGivenName() {
+			mr.Replace(i.userAttributeMap.givenName, []string{user.GetGivenName()})
+			updateNeeded = true
+		}
+	}
+	if user.PasswordProfile != nil && user.PasswordProfile.GetPassword() != "" {
 		if i.usePwModifyExOp {
 			if err := i.updateUserPassowrd(ctx, e.DN, user.PasswordProfile.GetPassword()); err != nil {
 				return nil, err
@@ -293,7 +305,7 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.
 		} else {
 			// password are hashed server side there is no need to check if the new password
 			// is actually different from the old one.
-			mr.Replace("userPassword", []string{*user.PasswordProfile.Password})
+			mr.Replace("userPassword", []string{user.PasswordProfile.GetPassword()})
 			updateNeeded = true
 		}
 	}
diff --git a/services/graph/pkg/identity/ldap_education_user.go b/services/graph/pkg/identity/ldap_education_user.go
index e346bd9897f..a0b32e57dc4 100644
--- a/services/graph/pkg/identity/ldap_education_user.go
+++ b/services/graph/pkg/identity/ldap_education_user.go
@@ -77,7 +77,108 @@ func (i *LDAP) DeleteEducationUser(ctx context.Context, nameOrID string) error {
 
 // UpdateEducationUser applies changes to given education user, identified by username or id
 func (i *LDAP) UpdateEducationUser(ctx context.Context, nameOrID string, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
-	return nil, errNotImplemented
+	logger := i.logger.SubloggerWithRequestID(ctx)
+	logger.Debug().Str("backend", "ldap").Msg("UpdateEducationUser")
+	if !i.writeEnabled {
+		return nil, ErrReadOnly
+	}
+	e, err := i.getEducationUserByNameOrID(nameOrID)
+	if err != nil {
+		return nil, err
+	}
+
+	var updateNeeded bool
+
+	// Don't allow updates of the ID
+	if user.GetId() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != user.GetId() {
+			return nil, errorcode.New(errorcode.NotAllowed, "changing the UserId is not allowed")
+		}
+	}
+	if user.GetOnPremisesSamAccountName() != "" {
+		if eu := e.GetEqualFoldAttributeValue(i.userAttributeMap.userName); eu != user.GetOnPremisesSamAccountName() {
+			e, err = i.changeUserName(ctx, e.DN, eu, user.GetOnPremisesSamAccountName())
+			if err != nil {
+				return nil, err
+			}
+			e, err = i.getEducationUserByDN(e.DN)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	mr := ldap.ModifyRequest{DN: e.DN}
+	if user.GetDisplayName() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName) != user.GetDisplayName() {
+			mr.Replace(i.userAttributeMap.displayName, []string{user.GetDisplayName()})
+			updateNeeded = true
+		}
+	}
+	if user.GetMail() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.mail) != user.GetMail() {
+			mr.Replace(i.userAttributeMap.mail, []string{user.GetMail()})
+			updateNeeded = true
+		}
+	}
+	if user.GetSurname() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.surname) != user.GetSurname() {
+			mr.Replace(i.userAttributeMap.surname, []string{user.GetSurname()})
+			updateNeeded = true
+		}
+	}
+	if user.GetGivenName() != "" {
+		if e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName) != user.GetGivenName() {
+			mr.Replace(i.userAttributeMap.givenName, []string{user.GetGivenName()})
+			updateNeeded = true
+		}
+	}
+	if user.PasswordProfile != nil && user.PasswordProfile.GetPassword() != "" {
+		if i.usePwModifyExOp {
+			if err := i.updateUserPassowrd(ctx, e.DN, user.PasswordProfile.GetPassword()); err != nil {
+				return nil, err
+			}
+		} else {
+			// password are hashed server side there is no need to check if the new password
+			// is actually different from the old one.
+			mr.Replace("userPassword", []string{user.PasswordProfile.GetPassword()})
+			updateNeeded = true
+		}
+	}
+	if user.GetPrimaryRole() != "" {
+		if e.GetEqualFoldAttributeValue(i.educationConfig.userAttributeMap.primaryRole) != user.GetPrimaryRole() {
+			mr.Replace(i.educationConfig.userAttributeMap.primaryRole, []string{user.GetPrimaryRole()})
+			updateNeeded = true
+		}
+	}
+	if identities, ok := user.GetIdentitiesOk(); ok {
+		attrValues := make([]string, 0, len(identities))
+		for _, identity := range identities {
+			identityStr, err := i.identityToLDAPAttrValue(identity)
+			if err != nil {
+				return nil, err
+			}
+			attrValues = append(attrValues, identityStr)
+		}
+		mr.Replace(i.educationConfig.userAttributeMap.identities, attrValues)
+		updateNeeded = true
+	}
+
+	if updateNeeded {
+		if err := i.conn.Modify(&mr); err != nil {
+			return nil, err
+		}
+	}
+
+	// Read	back user from LDAP to get the generated UUID
+	e, err = i.getEducationUserByDN(e.DN)
+	if err != nil {
+		return nil, err
+	}
+
+	returnUser := i.createEducationUserModelFromLDAP(e)
+
+	return returnUser, nil
 }
 
 // GetEducationUser implements the EducationBackend interface for the LDAP backend.
@@ -189,11 +290,10 @@ func (i *LDAP) educationUserToLDAPAttrValues(user libregraph.EducationUser, attr
 	}
 	if identities, ok := user.GetIdentitiesOk(); ok {
 		for _, identity := range identities {
-			// TODO add support for the "signInType" of objectIdentity
-			if identity.GetIssuer() == "" || identity.GetIssuerAssignedId() == "" {
-				return nil, fmt.Errorf("missing Attribute for objectIdentity")
+			identityStr, err := i.identityToLDAPAttrValue(identity)
+			if err != nil {
+				return nil, err
 			}
-			identityStr := fmt.Sprintf(" $ %s $ %s", identity.GetIssuer(), identity.GetIssuerAssignedId())
 			attrs[i.educationConfig.userAttributeMap.identities] = append(
 				attrs[i.educationConfig.userAttributeMap.identities],
 				identityStr,
@@ -203,6 +303,14 @@ func (i *LDAP) educationUserToLDAPAttrValues(user libregraph.EducationUser, attr
 	attrs["objectClass"] = append(attrs["objectClass"], i.educationConfig.userObjectClass)
 	return attrs, nil
 }
+func (i *LDAP) identityToLDAPAttrValue(identity libregraph.ObjectIdentity) (string, error) {
+	// TODO add support for the "signInType" of objectIdentity
+	if identity.GetIssuer() == "" || identity.GetIssuerAssignedId() == "" {
+		return "", fmt.Errorf("missing Attribute for objectIdentity")
+	}
+	identityStr := fmt.Sprintf(" $ %s $ %s", identity.GetIssuer(), identity.GetIssuerAssignedId())
+	return identityStr, nil
+}
 
 func (i *LDAP) educationUserToAddRequest(user libregraph.EducationUser) (*ldap.AddRequest, error) {
 	plainUser := i.educationUserToUser(user)
@@ -234,6 +342,9 @@ func (i *LDAP) getEducationUserAttrTypes() []string {
 		i.userAttributeMap.id,
 		i.userAttributeMap.mail,
 		i.userAttributeMap.userName,
+		i.userAttributeMap.surname,
+		i.userAttributeMap.givenName,
+		i.userAttributeMap.accountEnabled,
 		i.educationConfig.userAttributeMap.identities,
 		i.educationConfig.userAttributeMap.primaryRole,
 		i.educationConfig.memberOfSchoolAttribute,
diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go
index 398bc29c38c..9842c10ca21 100644
--- a/services/graph/pkg/service/v0/drives.go
+++ b/services/graph/pkg/service/v0/drives.go
@@ -444,6 +444,10 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
 			logger.Debug().Interface("id", rid).Msg("could not update drive, permission denied")
 			errorcode.NotAllowed.Render(w, r, http.StatusForbidden, resp.GetStatus().GetMessage())
 			return
+		case cs3rpc.Code_CODE_INVALID_ARGUMENT:
+			logger.Debug().Interface("id", rid).Msg("could not update drive, invalid argument")
+			errorcode.NotAllowed.Render(w, r, http.StatusBadRequest, resp.GetStatus().GetMessage())
+			return
 		default:
 			logger.Debug().Interface("id", rid).Str("grpc", resp.GetStatus().GetMessage()).Msg("could not update drive: grpc error")
 			errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, resp.GetStatus().GetMessage())
diff --git a/services/idp/pkg/config/config.go b/services/idp/pkg/config/config.go
index 7d3854ccccd..b7075631c9a 100644
--- a/services/idp/pkg/config/config.go
+++ b/services/idp/pkg/config/config.go
@@ -73,7 +73,7 @@ type Settings struct {
 
 	Iss string `yaml:"iss" env:"OCIS_URL;OCIS_OIDC_ISSUER;IDP_ISS" desc:"The OIDC issuer URL to use."`
 
-	IdentityManager string `yaml:"identity_manager" env:"IDP_IDENTITY_MANAGER" desc:"The identity manager implementation to use. Supported identity managers are 'ldap', 'cs3', 'kc', 'libregraph', 'cookie' and 'guest'."`
+	IdentityManager string `yaml:"identity_manager" env:"IDP_IDENTITY_MANAGER" desc:"The identity manager implementation to use. Supported identity managers are 'ldap', 'cs3', 'libregraph' and 'guest'."`
 
 	URIBasePath string `yaml:"uri_base_path" env:"IDP_URI_BASE_PATH" desc:"IDP uri base path (defaults to \"\")."`
 
diff --git a/services/idp/pkg/service/v0/service.go b/services/idp/pkg/service/v0/service.go
index 231a802f5f7..beaa125297f 100644
--- a/services/idp/pkg/service/v0/service.go
+++ b/services/idp/pkg/service/v0/service.go
@@ -14,7 +14,6 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/libregraph/lico/bootstrap"
 	guestBackendSupport "github.com/libregraph/lico/bootstrap/backends/guest"
-	kcBackendSupport "github.com/libregraph/lico/bootstrap/backends/kc"
 	ldapBackendSupport "github.com/libregraph/lico/bootstrap/backends/ldap"
 	libreGraphBackendSupport "github.com/libregraph/lico/bootstrap/backends/libregraph"
 	licoconfig "github.com/libregraph/lico/config"
@@ -74,7 +73,6 @@ func NewService(opts ...Option) Service {
 		}
 	default:
 		guestBackendSupport.MustRegister()
-		kcBackendSupport.MustRegister()
 		libreGraphBackendSupport.MustRegister()
 	}
 
diff --git a/services/sharing/pkg/config/config.go b/services/sharing/pkg/config/config.go
index c55c5e783fb..aae8dc27887 100644
--- a/services/sharing/pkg/config/config.go
+++ b/services/sharing/pkg/config/config.go
@@ -21,10 +21,11 @@ type Config struct {
 
 	SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"SHARING_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token."`
 
-	UserSharingDriver    string               `yaml:"user_sharing_driver" env:"SHARING_USER_DRIVER" desc:"Driver to be used to persist shares. Supported values are 'jsoncs3', 'json', 'cs3' and 'owncloudsql'."`
-	UserSharingDrivers   UserSharingDrivers   `yaml:"user_sharing_drivers"`
-	PublicSharingDriver  string               `yaml:"public_sharing_driver" env:"SHARING_PUBLIC_DRIVER" desc:"Driver to be used to persist public shares. Supported values are 'jsoncs3', 'json' and 'cs3'."`
-	PublicSharingDrivers PublicSharingDrivers `yaml:"public_sharing_drivers"`
+	UserSharingDriver              string               `yaml:"user_sharing_driver" env:"SHARING_USER_DRIVER" desc:"Driver to be used to persist shares. Supported values are 'jsoncs3', 'json', 'cs3' and 'owncloudsql'."`
+	UserSharingDrivers             UserSharingDrivers   `yaml:"user_sharing_drivers"`
+	PublicSharingDriver            string               `yaml:"public_sharing_driver" env:"SHARING_PUBLIC_DRIVER" desc:"Driver to be used to persist public shares. Supported values are 'jsoncs3', 'json' and 'cs3'."`
+	PublicSharingDrivers           PublicSharingDrivers `yaml:"public_sharing_drivers"`
+	WriteableShareMustHavePassword bool                 `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"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."`
 
 	Supervised bool            `yaml:"-"`
 	Context    context.Context `yaml:"-"`
diff --git a/services/sharing/pkg/revaconfig/config.go b/services/sharing/pkg/revaconfig/config.go
index 8af76974c55..082f5773253 100644
--- a/services/sharing/pkg/revaconfig/config.go
+++ b/services/sharing/pkg/revaconfig/config.go
@@ -103,11 +103,12 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} {
 							"machine_auth_apikey": cfg.PublicSharingDrivers.CS3.SystemUserAPIKey,
 						},
 						"jsoncs3": map[string]interface{}{
-							"gateway_addr":        cfg.Reva.Address,
-							"provider_addr":       cfg.PublicSharingDrivers.JSONCS3.ProviderAddr,
-							"service_user_id":     cfg.PublicSharingDrivers.JSONCS3.SystemUserID,
-							"service_user_idp":    cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP,
-							"machine_auth_apikey": cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey,
+							"gateway_addr":                       cfg.Reva.Address,
+							"provider_addr":                      cfg.PublicSharingDrivers.JSONCS3.ProviderAddr,
+							"service_user_id":                    cfg.PublicSharingDrivers.JSONCS3.SystemUserID,
+							"service_user_idp":                   cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP,
+							"machine_auth_apikey":                cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey,
+							"writeable_share_must_have_password": cfg.WriteableShareMustHavePassword,
 						},
 					},
 				},
diff --git a/services/userlog/pkg/command/server.go b/services/userlog/pkg/command/server.go
index 7ca044d297d..6f09a537159 100644
--- a/services/userlog/pkg/command/server.go
+++ b/services/userlog/pkg/command/server.go
@@ -25,36 +25,17 @@ import (
 
 // all events we care about
 var _registeredEvents = []events.Unmarshaller{
-	// file related
-	events.UploadReady{},
-	events.ContainerCreated{},
-	events.FileTouched{},
-	events.FileDownloaded{},
-	events.FileVersionRestored{},
-	events.ItemMoved{},
-	events.ItemTrashed{},
-	events.ItemPurged{},
-	events.ItemRestored{},
-
 	// space related
-	events.SpaceCreated{},
-	events.SpaceRenamed{},
-	events.SpaceEnabled{},
 	events.SpaceDisabled{},
 	events.SpaceDeleted{},
 	events.SpaceShared{},
 	events.SpaceUnshared{},
-	events.SpaceUpdated{},
 	events.SpaceMembershipExpired{},
 
 	// share related
 	events.ShareCreated{},
-	// events.ShareRemoved{}, // TODO: ShareRemoved doesn't hold sharee information
-	events.ShareUpdated{},
+	events.ShareRemoved{},
 	events.ShareExpired{},
-	events.LinkCreated{},
-	// events.LinkRemoved{}, // TODO: LinkRemoved doesn't hold sharee information
-	events.LinkUpdated{},
 }
 
 // Server is the entrypoint for the server command.
diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go
new file mode 100644
index 00000000000..a3463508944
--- /dev/null
+++ b/services/userlog/pkg/service/conversion.go
@@ -0,0 +1,249 @@
+package service
+
+import (
+	"bytes"
+	"errors"
+	"text/template"
+	"time"
+
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
+	storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/cs3org/reva/v2/pkg/events"
+	"github.com/cs3org/reva/v2/pkg/storagespace"
+	"github.com/cs3org/reva/v2/pkg/utils"
+	ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0"
+)
+
+var (
+	_resourceTypeSpace = "storagespace"
+	_resourceTypeShare = "share"
+)
+
+// OC10Notification is the oc10 style representation of an event
+// some fields are left out for simplicity
+type OC10Notification struct {
+	EventID        string                 `json:"notification_id"`
+	Service        string                 `json:"app"`
+	UserName       string                 `json:"user"`
+	Timestamp      string                 `json:"datetime"`
+	ResourceID     string                 `json:"object_id"`
+	ResourceType   string                 `json:"object_type"`
+	Subject        string                 `json:"subject"`
+	SubjectRaw     string                 `json:"subjectRich"`
+	Message        string                 `json:"message"`
+	MessageRaw     string                 `json:"messageRich"`
+	MessageDetails map[string]interface{} `json:"messageRichParameters"`
+}
+
+// ConvertEvent converts an eventhistory event to an OC10Notification
+func (ul *UserlogService) ConvertEvent(event *ehmsg.Event) (OC10Notification, error) {
+	etype, ok := ul.registeredEvents[event.Type]
+	if !ok {
+		// this should not happen
+		return OC10Notification{}, errors.New("eventtype not registered")
+	}
+
+	einterface, err := etype.Unmarshal(event.Event)
+	if err != nil {
+		// this shouldn't happen either
+		return OC10Notification{}, errors.New("cant unmarshal event")
+	}
+
+	switch ev := einterface.(type) {
+	default:
+		return OC10Notification{}, errors.New("unknown event type")
+	// space related
+	case events.SpaceDisabled:
+		return ul.spaceMessage(event.Id, SpaceDisabled, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp)
+	case events.SpaceDeleted:
+		return ul.spaceDeletedMessage(event.Id, ev.Executant, ev.ID.GetOpaqueId(), ev.SpaceName, ev.Timestamp)
+	case events.SpaceShared:
+		return ul.spaceMessage(event.Id, SpaceShared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp)
+	case events.SpaceUnshared:
+		return ul.spaceMessage(event.Id, SpaceUnshared, ev.Executant, ev.ID.GetOpaqueId(), ev.Timestamp)
+	case events.SpaceMembershipExpired:
+		return ul.spaceMessage(event.Id, SpaceMembershipExpired, ev.SpaceOwner, ev.SpaceID.GetOpaqueId(), ev.ExpiredAt)
+
+	// share related
+	case events.ShareCreated:
+		return ul.shareMessage(event.Id, ShareCreated, ev.Executant, ev.ItemID, ev.ShareID, utils.TSToTime(ev.CTime))
+	case events.ShareExpired:
+		return ul.shareMessage(event.Id, ShareExpired, ev.ShareOwner, ev.ItemID, ev.ShareID, ev.ExpiredAt)
+	case events.ShareRemoved:
+		return ul.shareMessage(event.Id, ShareRemoved, ev.Executant, ev.ItemID, ev.ShareID, ev.Timestamp)
+	}
+}
+
+func (ul *UserlogService) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time) (OC10Notification, error) {
+	_, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey)
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	subj, subjraw, msg, msgraw, err := ul.composeMessage(SpaceDeleted, map[string]string{
+		"username":  user.GetDisplayName(),
+		"spacename": spacename,
+	})
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	details := ul.getDetails(user, nil, nil, nil)
+	details["space"] = map[string]string{
+		"id":   spaceid,
+		"name": spacename,
+	}
+
+	return OC10Notification{
+		EventID:        eventid,
+		Service:        ul.cfg.Service.Name,
+		UserName:       user.GetUsername(),
+		Timestamp:      ts.Format(time.RFC3339Nano),
+		ResourceID:     spaceid,
+		ResourceType:   _resourceTypeSpace,
+		Subject:        subj,
+		SubjectRaw:     subjraw,
+		Message:        msg,
+		MessageRaw:     msgraw,
+		MessageDetails: details,
+	}, nil
+}
+
+func (ul *UserlogService) spaceMessage(eventid string, eventname string, executant *user.UserId, spaceid string, ts time.Time) (OC10Notification, error) {
+	ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey)
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	space, err := ul.getSpace(ctx, spaceid)
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{
+		"username":  user.GetDisplayName(),
+		"spacename": space.GetName(),
+	})
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	return OC10Notification{
+		EventID:        eventid,
+		Service:        ul.cfg.Service.Name,
+		UserName:       user.GetUsername(),
+		Timestamp:      ts.Format(time.RFC3339Nano),
+		ResourceID:     spaceid,
+		ResourceType:   _resourceTypeSpace,
+		Subject:        subj,
+		SubjectRaw:     subjraw,
+		Message:        msg,
+		MessageRaw:     msgraw,
+		MessageDetails: ul.getDetails(user, space, nil, nil),
+	}, nil
+}
+
+func (ul *UserlogService) shareMessage(eventid string, eventname string, executant *user.UserId, resourceid *storageprovider.ResourceId, shareid *collaboration.ShareId, ts time.Time) (OC10Notification, error) {
+	ctx, user, err := utils.Impersonate(executant, ul.gwClient, ul.cfg.MachineAuthAPIKey)
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	info, err := ul.getResource(ctx, resourceid)
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	subj, subjraw, msg, msgraw, err := ul.composeMessage(eventname, map[string]string{
+		"username":     user.GetDisplayName(),
+		"resourcename": info.GetName(),
+	})
+	if err != nil {
+		return OC10Notification{}, err
+	}
+
+	return OC10Notification{
+		EventID:        eventid,
+		Service:        ul.cfg.Service.Name,
+		UserName:       user.GetUsername(),
+		Timestamp:      ts.Format(time.RFC3339Nano),
+		ResourceID:     storagespace.FormatResourceID(*info.GetId()),
+		ResourceType:   _resourceTypeShare,
+		Subject:        subj,
+		SubjectRaw:     subjraw,
+		Message:        msg,
+		MessageRaw:     msgraw,
+		MessageDetails: ul.getDetails(user, nil, info, shareid),
+	}, nil
+}
+
+func (ul *UserlogService) composeMessage(eventname string, vars map[string]string) (string, string, string, string, error) {
+	tpl, ok := _templates[eventname]
+	if !ok {
+		return "", "", "", "", errors.New("unknown template name")
+	}
+
+	subject := ul.executeTemplate(tpl.Subject, vars)
+
+	subjectraw := ul.executeTemplate(tpl.Subject, map[string]string{
+		"username":     "{user}",
+		"spacename":    "{space}",
+		"resourcename": "{resource}",
+	})
+
+	message := ul.executeTemplate(tpl.Message, vars)
+
+	messageraw := ul.executeTemplate(tpl.Message, map[string]string{
+		"username":     "{user}",
+		"spacename":    "{space}",
+		"resourcename": "{resource}",
+	})
+
+	return subject, subjectraw, message, messageraw, nil
+
+}
+
+func (ul *UserlogService) getDetails(user *user.User, space *storageprovider.StorageSpace, item *storageprovider.ResourceInfo, shareid *collaboration.ShareId) map[string]interface{} {
+	details := make(map[string]interface{})
+
+	if user != nil {
+		details["user"] = map[string]string{
+			"id":          user.GetId().GetOpaqueId(),
+			"name":        user.GetUsername(),
+			"displayname": user.GetDisplayName(),
+		}
+	}
+
+	if space != nil {
+		details["space"] = map[string]string{
+			"id":   space.GetId().GetOpaqueId(),
+			"name": space.GetName(),
+		}
+	}
+
+	if item != nil {
+		details["resource"] = map[string]string{
+			"id":   storagespace.FormatResourceID(*item.GetId()),
+			"name": item.GetName(),
+		}
+	}
+
+	if shareid != nil {
+		details["share"] = map[string]string{
+			"id": shareid.GetOpaqueId(),
+		}
+	}
+
+	return details
+}
+
+func (ul *UserlogService) executeTemplate(tpl *template.Template, vars map[string]string) string {
+	var writer bytes.Buffer
+	if err := tpl.Execute(&writer, vars); err != nil {
+		ul.log.Error().Err(err).Str("templateName", tpl.Name()).Msg("cannot execute template")
+		return ""
+	}
+
+	return writer.String()
+}
diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go
index 4ad2011ac12..725275e04d4 100644
--- a/services/userlog/pkg/service/http.go
+++ b/services/userlog/pkg/service/http.go
@@ -1,17 +1,10 @@
 package service
 
 import (
-	"context"
 	"encoding/json"
-	"errors"
-	"fmt"
 	"net/http"
-	"time"
 
-	storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	revactx "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/cs3org/reva/v2/pkg/events"
-	ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0"
 )
 
 // ServeHTTP fulfills Handler interface
@@ -37,7 +30,7 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request
 
 	resp := GetEventResponseOC10{}
 	for _, e := range evs {
-		noti, err := ul.convertEvent(r.Context(), e)
+		noti, err := ul.ConvertEvent(e)
 		if err != nil {
 			ul.log.Error().Err(err).Str("eventid", e.Id).Str("eventtype", e.Type).Msg("failed to convert event")
 			continue
@@ -60,14 +53,14 @@ func (ul *UserlogService) HandleDeleteEvents(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	var ids []string
-	if err := json.NewDecoder(r.Body).Decode(&ids); err != nil {
+	var req DeleteEventsRequest
+	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 		ul.log.Error().Err(err).Int("returned statuscode", http.StatusBadRequest).Msg("request body is malformed")
 		w.WriteHeader(http.StatusBadRequest)
 		return
 	}
 
-	if err := ul.DeleteEvents(u.GetId().GetOpaqueId(), ids); err != nil {
+	if err := ul.DeleteEvents(u.GetId().GetOpaqueId(), req.IDs); err != nil {
 		ul.log.Error().Err(err).Int("returned statuscode", http.StatusInternalServerError).Msg("delete events failed")
 		w.WriteHeader(http.StatusInternalServerError)
 		return
@@ -76,140 +69,6 @@ func (ul *UserlogService) HandleDeleteEvents(w http.ResponseWriter, r *http.Requ
 	w.WriteHeader(http.StatusOK)
 }
 
-func (ul *UserlogService) convertEvent(ctx context.Context, event *ehmsg.Event) (OC10Notification, error) {
-	etype, ok := ul.registeredEvents[event.Type]
-	if !ok {
-		// this should not happen
-		return OC10Notification{}, errors.New("eventtype not registered")
-	}
-
-	einterface, err := etype.Unmarshal(event.Event)
-	if err != nil {
-		// this shouldn't happen either
-		return OC10Notification{}, errors.New("cant unmarshal event")
-	}
-
-	noti := OC10Notification{
-		EventID:   event.Id,
-		Service:   "userlog",
-		Timestamp: time.Now().Format(time.RFC3339Nano),
-	}
-
-	// TODO: strange bug with getting space -> fix postponed to make master panic-free
-	var space storageprovider.StorageSpace
-	switch ev := einterface.(type) {
-	// file related
-	case events.UploadReady:
-		noti.UserID = ev.ExecutingUser.GetId().GetOpaqueId()
-		noti.Subject = "File uploaded"
-		noti.Message = fmt.Sprintf("File '%s' was uploaded to space '%s' by user '%s'", ev.Filename, space.GetName(), ev.ExecutingUser.GetUsername())
-	case events.ContainerCreated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Folder created"
-		noti.Message = fmt.Sprintf("Folder '%s' was created", ev.Ref.GetPath())
-	case events.FileTouched:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File touched"
-		noti.Message = fmt.Sprintf("File '%s' was touched", ev.Ref.GetPath())
-	case events.FileDownloaded:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File downloaded"
-		noti.Message = fmt.Sprintf("File '%s' was downloaded", ev.Ref.GetPath())
-	case events.FileVersionRestored:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File version restored"
-		noti.Message = fmt.Sprintf("An older version of file '%s' was restored", ev.Ref.GetPath())
-	case events.ItemMoved:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File moved"
-		noti.Message = fmt.Sprintf("File '%s' was moved from '%s'", ev.Ref.GetPath(), ev.OldReference.GetPath())
-	case events.ItemTrashed:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File trashed"
-		noti.Message = fmt.Sprintf("File '%s' was trashed", ev.Ref.GetPath())
-	case events.ItemPurged:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File purged"
-		noti.Message = fmt.Sprintf("File '%s' was purged", ev.Ref.GetPath())
-	case events.ItemRestored:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "File restored"
-		noti.Message = fmt.Sprintf("File '%s' was restored", ev.Ref.GetPath())
-
-	// space related
-	case events.SpaceCreated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space created"
-		noti.Message = fmt.Sprintf("Space '%s' was created", ev.Name)
-	case events.SpaceRenamed:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space renamed"
-		noti.Message = fmt.Sprintf("Space '%s' was renamed", ev.Name)
-	case events.SpaceEnabled:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space enabled"
-		noti.Message = fmt.Sprintf("Space '%s' was renamed", space.Name)
-	case events.SpaceDisabled:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space disabled"
-		noti.Message = fmt.Sprintf("Space '%s' was disabled", space.Name)
-	case events.SpaceDeleted:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space deleted"
-		noti.Message = fmt.Sprintf("Space '%s' was deleted", space.Name)
-	case events.SpaceShared:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space shared"
-		noti.Message = fmt.Sprintf("Space '%s' was shared", space.Name)
-	case events.SpaceUnshared:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space unshared"
-		noti.Message = fmt.Sprintf("Space '%s' was unshared", space.Name)
-	case events.SpaceUpdated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Space updated"
-		noti.Message = fmt.Sprintf("Space '%s' was updated", space.Name)
-	case events.SpaceMembershipExpired:
-		noti.UserID = ""
-		noti.Subject = "Space membership expired"
-		noti.Message = fmt.Sprintf("A spacemembership for space '%s' has expired", space.Name)
-
-	// share related
-	case events.ShareCreated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Share received"
-		noti.Message = fmt.Sprintf("A file was shared in space %s", space.Name)
-	case events.ShareUpdated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Share updated"
-		noti.Message = fmt.Sprintf("A share was updated in space %s", space.Name)
-	case events.ShareExpired:
-		noti.Subject = "Share expired"
-		noti.Message = fmt.Sprintf("A share has expired in space %s", space.Name)
-	case events.LinkCreated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Share received"
-		noti.Message = fmt.Sprintf("A link was created in space %s", space.Name)
-	case events.LinkUpdated:
-		noti.UserID = ev.Executant.GetOpaqueId()
-		noti.Subject = "Share received"
-		noti.Message = fmt.Sprintf("A link was updated in space %s", space.Name)
-	}
-
-	return noti, nil
-}
-
-// OC10Notification is the oc10 style representation of an event
-// some fields are left out for simplicity
-type OC10Notification struct {
-	EventID   string `json:"notification_id"`
-	Service   string `json:"app"`
-	Timestamp string `json:"datetime"`
-	UserID    string `json:"user"`
-	Subject   string `json:"subject"`
-	Message   string `json:"message"`
-}
-
 // GetEventResponseOC10 is the response from GET events endpoint in oc10 style
 type GetEventResponseOC10 struct {
 	OCS struct {
@@ -221,3 +80,8 @@ type GetEventResponseOC10 struct {
 		Data []OC10Notification `json:"data"`
 	} `json:"ocs"`
 }
+
+// DeleteEventsRequest is the expected body for the delete request
+type DeleteEventsRequest struct {
+	IDs []string `json:"ids"`
+}
diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go
index 838f75dc9cf..3a8e8559974 100644
--- a/services/userlog/pkg/service/service.go
+++ b/services/userlog/pkg/service/service.go
@@ -25,7 +25,6 @@ import (
 // UserlogService is the service responsible for user activities
 type UserlogService struct {
 	log              log.Logger
-	ch               <-chan events.Event
 	m                *chi.Mux
 	store            store.Store
 	cfg              *config.Config
@@ -52,7 +51,6 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) {
 
 	ul := &UserlogService{
 		log:              o.Logger,
-		ch:               ch,
 		m:                o.Mux,
 		store:            o.Store,
 		cfg:              o.Config,
@@ -71,80 +69,50 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) {
 		r.Delete("/*", ul.HandleDeleteEvents)
 	})
 
-	go ul.MemorizeEvents()
+	go ul.MemorizeEvents(ch)
 
 	return ul, nil
 }
 
 // MemorizeEvents stores eventIDs a user wants to receive
-func (ul *UserlogService) MemorizeEvents() {
-	for event := range ul.ch {
+func (ul *UserlogService) MemorizeEvents(ch <-chan events.Event) {
+	for event := range ch {
 		// for each event we need to:
 		// I) find users eligible to receive the event
 		var (
 			users []string
 			err   error
 		)
+
 		switch e := event.Event.(type) {
 		default:
 			err = errors.New("unhandled event")
-
-		// file related
-		case events.UploadReady:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.FileRef.GetResourceId().GetSpaceId(), viewer)
-		case events.ContainerCreated:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), viewer)
-		case events.FileTouched:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), viewer)
-		case events.FileDownloaded:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.Ref.GetResourceId().GetSpaceId(), viewer) // no space owner in event
-		case events.FileVersionRestored:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), editor)
-		case events.ItemMoved:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), viewer)
-		case events.ItemTrashed:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), viewer)
-		case events.ItemPurged:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.Ref.GetResourceId().GetSpaceId(), editor) // no space owner in event
-		case events.ItemRestored:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.SpaceOwner), e.Ref.GetResourceId().GetSpaceId(), viewer)
-
 		// space related // TODO: how to find spaceadmins?
-		case events.SpaceCreated:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer)
-		case events.SpaceRenamed:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer)
-		case events.SpaceEnabled:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer)
 		case events.SpaceDisabled:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), manager)
+			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer)
 		case events.SpaceDeleted:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), manager)
+			for u, _ := range e.FinalMembers {
+				users = append(users, u)
+			}
 		case events.SpaceShared:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), manager)
+			users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
 		case events.SpaceUnshared:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), manager)
-		case events.SpaceUpdated:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), manager)
+			users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
 		case events.SpaceMembershipExpired:
-			users, err = ul.resolveShare(ul.impersonate(e.SpaceOwner), e.GranteeUserID, e.GranteeGroupID, e.SpaceID.GetOpaqueId())
+			users, err = ul.resolveID(ul.impersonate(e.SpaceOwner), e.GranteeUserID, e.GranteeGroupID)
 
 		// share related
 		case events.ShareCreated:
-			users, err = ul.resolveShare(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID, e.ItemID.GetSpaceId())
-		case events.ShareUpdated:
-			users, err = ul.resolveShare(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID, e.ItemID.GetSpaceId())
+			users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
+		case events.ShareRemoved:
+			users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
 		case events.ShareExpired:
-			users, err = ul.resolveShare(ul.impersonate(e.ShareOwner), e.GranteeUserID, e.GranteeGroupID, e.ItemID.GetSpaceId())
-		case events.LinkCreated:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ItemID.GetOpaqueId(), editor)
-		case events.LinkUpdated:
-			users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ItemID.GetOpaqueId(), editor)
-
+			users, err = ul.resolveID(ul.impersonate(e.ShareOwner), e.GranteeUserID, e.GranteeGroupID)
 		}
 
 		if err != nil {
-			ul.log.Error().Err(err).Interface("event", event).Msg("error gathering members for event")
+			// TODO: Find out why this errors on ci pipeline
+			ul.log.Debug().Err(err).Interface("event", event).Msg("error gathering members for event")
 			continue
 		}
 
@@ -218,20 +186,6 @@ func (ul *UserlogService) DeleteEvents(userid string, evids []string) error {
 	})
 }
 
-func (ul *UserlogService) impersonate(u *user.UserId) context.Context {
-	if u == nil {
-		ul.log.Debug().Msg("cannot impersonate nil user")
-		return nil
-	}
-
-	ctx, _, err := utils.Impersonate(u, ul.gwClient, ul.cfg.MachineAuthAPIKey)
-	if err != nil {
-		ul.log.Error().Err(err).Str("userid", u.GetOpaqueId()).Msg("failed to impersonate user")
-		return nil
-	}
-	return ctx
-}
-
 func (ul *UserlogService) addEventsToUser(userid string, eventids ...string) error {
 	return ul.alterUserEventList(userid, func(ids []string) []string {
 		return append(ids, eventids...)
@@ -318,23 +272,6 @@ func (ul *UserlogService) findSpaceMembers(ctx context.Context, spaceID string,
 	return users, nil
 }
 
-func (ul *UserlogService) getSpace(ctx context.Context, spaceID string) (*storageprovider.StorageSpace, error) {
-	res, err := ul.gwClient.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID))
-	if err != nil {
-		return nil, err
-	}
-
-	if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		return nil, fmt.Errorf("Unexpected status code while getting space: %v", res.GetStatus().GetCode())
-	}
-
-	if len(res.StorageSpaces) == 0 {
-		return nil, fmt.Errorf("error getting storage space %s: no space returned", spaceID)
-	}
-
-	return res.StorageSpaces[0], nil
-}
-
 func (ul *UserlogService) gatherSpaceMembers(ctx context.Context, space *storageprovider.StorageSpace, hasRequiredRole permissionChecker) ([]string, error) {
 	var permissionsMap map[string]*storageprovider.ResourcePermissions
 	if err := utils.ReadJSONFromOpaque(space.GetOpaque(), "grants", &permissionsMap); err != nil {
@@ -378,49 +315,99 @@ func (ul *UserlogService) gatherSpaceMembers(ctx context.Context, space *storage
 	return users, nil
 }
 
+func (ul *UserlogService) resolveID(ctx context.Context, userid *user.UserId, groupid *group.GroupId) ([]string, error) {
+	if userid != nil {
+		return []string{userid.GetOpaqueId()}, nil
+	}
+
+	return ul.resolveGroup(ctx, groupid.GetOpaqueId())
+}
+
 // resolves the users of a group
 func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]string, error) {
-	if ctx == nil {
-		return nil, errors.New("need authenticated context to resolve groups")
+	grp, err := ul.getGroup(ctx, groupID)
+	if err != nil {
+		return nil, err
 	}
 
-	r, err := ul.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupID}})
+	var userIDs []string
+	for _, m := range grp.GetMembers() {
+		userIDs = append(userIDs, m.GetOpaqueId())
+	}
+
+	return userIDs, nil
+}
+
+func (ul *UserlogService) impersonate(u *user.UserId) context.Context {
+	if u == nil {
+		ul.log.Debug().Msg("cannot impersonate nil user")
+		return nil
+	}
+
+	ctx, _, err := utils.Impersonate(u, ul.gwClient, ul.cfg.MachineAuthAPIKey)
+	if err != nil {
+		ul.log.Error().Err(err).Str("userid", u.GetOpaqueId()).Msg("failed to impersonate user")
+		return nil
+	}
+	return ctx
+}
+
+func (ul *UserlogService) getSpace(ctx context.Context, spaceID string) (*storageprovider.StorageSpace, error) {
+	res, err := ul.gwClient.ListStorageSpaces(ctx, listStorageSpaceRequest(spaceID))
 	if err != nil {
 		return nil, err
 	}
 
-	if r.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		return nil, fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
+	if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
+		return nil, fmt.Errorf("Unexpected status code while getting space: %v", res.GetStatus().GetCode())
 	}
 
-	var userIDs []string
-	for _, m := range r.GetGroup().GetMembers() {
-		userIDs = append(userIDs, m.GetOpaqueId())
+	if len(res.StorageSpaces) == 0 {
+		return nil, fmt.Errorf("error getting storage space %s: no space returned", spaceID)
 	}
 
-	return userIDs, nil
+	return res.StorageSpaces[0], nil
 }
 
-func (ul *UserlogService) resolveID(ctx context.Context, userid *user.UserId, groupid *group.GroupId) ([]string, error) {
-	if userid != nil {
-		return []string{userid.GetOpaqueId()}, nil
+func (ul *UserlogService) getUser(ctx context.Context, userid *user.UserId) (*user.User, error) {
+	getUserResponse, err := ul.gwClient.GetUser(context.Background(), &user.GetUserRequest{
+		UserId: userid,
+	})
+	if err != nil {
+		return nil, err
 	}
 
-	return ul.resolveGroup(ctx, groupid.GetOpaqueId())
+	if getUserResponse.Status.Code != rpc.Code_CODE_OK {
+		return nil, fmt.Errorf("error getting user: %s", getUserResponse.Status.Message)
+	}
+
+	return getUserResponse.GetUser(), nil
 }
 
-func (ul *UserlogService) resolveShare(ctx context.Context, userid *user.UserId, groupid *group.GroupId, spaceid string) ([]string, error) {
-	users, err := ul.resolveID(ctx, userid, groupid)
+func (ul *UserlogService) getGroup(ctx context.Context, groupid string) (*group.Group, error) {
+	r, err := ul.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: &group.GroupId{OpaqueId: groupid}})
 	if err != nil {
 		return nil, err
 	}
 
-	usr, err := ul.findSpaceMembers(ctx, spaceid, editor)
+	if r.GetStatus().GetCode() != rpc.Code_CODE_OK {
+		return nil, fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
+	}
+
+	return r.GetGroup(), nil
+}
+
+func (ul *UserlogService) getResource(ctx context.Context, resourceid *storageprovider.ResourceId) (*storageprovider.ResourceInfo, error) {
+	res, err := ul.gwClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: resourceid}})
 	if err != nil {
 		return nil, err
 	}
 
-	return append(users, usr...), nil
+	if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
+		return nil, fmt.Errorf("Unexpected status code while getting space: %v", res.GetStatus().GetCode())
+	}
+
+	return res.GetInfo(), nil
 }
 
 func listStorageSpaceRequest(spaceID string) *storageprovider.ListStorageSpacesRequest {
diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go
new file mode 100644
index 00000000000..bc9bf33d2b7
--- /dev/null
+++ b/services/userlog/pkg/service/templates.go
@@ -0,0 +1,65 @@
+package service
+
+import "text/template"
+
+// the available templates
+var (
+	SpaceShared        = "space-shared"
+	SpaceSharedSubject = "Space shared"
+	SpaceSharedMessage = "{{ .username }} added you to Space {{ .spacename }}"
+
+	SpaceUnshared        = "space-unshared"
+	SpaceUnsharedSubject = "Removed from Space"
+	SpaceUnsharedMessage = "{{ .username }} removed you from Space {{ .spacename }}"
+
+	SpaceDisabled        = "space-disabled"
+	SpaceDisabledSubject = "Space disabled"
+	SpaceDisabledMessage = "{{ .username }} disabled Space {{ .spacename }}"
+
+	SpaceDeleted        = "space-deleted"
+	SpaceDeletedSubject = "Space deleted"
+	SpaceDeletedMessage = "{{ .username }} deleted Space {{ .spacename }}"
+
+	SpaceMembershipExpired        = "space-membership-expired"
+	SpaceMembershipExpiredSubject = "Membership expired"
+	SpaceMembershipExpiredMessage = "Access to Space {{ .spacename }} lost"
+
+	ShareCreated        = "item-shared"
+	ShareCreatedSubject = "Resource shared"
+	ShareCreatedMessage = "{{ .username }} shared {{ .resourcename }} with you"
+
+	ShareRemoved        = "item-unshared"
+	ShareRemovedSubject = "Resource unshared"
+	ShareRemovedMessage = "{{ .username }} unshared {{ .resourcename }} with you"
+
+	ShareExpired        = "share-expired"
+	ShareExpiredSubject = "Share expired"
+	ShareExpiredMessage = "Access to {{ .resourcename }} expired"
+)
+
+// rendered templates
+var (
+	_templates = map[string]NotificationTemplate{
+		SpaceShared:            notiTmpl(SpaceSharedSubject, SpaceSharedMessage),
+		SpaceUnshared:          notiTmpl(SpaceUnsharedSubject, SpaceUnsharedMessage),
+		SpaceDisabled:          notiTmpl(SpaceDisabledSubject, SpaceDisabledMessage),
+		SpaceDeleted:           notiTmpl(SpaceDeletedSubject, SpaceDeletedMessage),
+		SpaceMembershipExpired: notiTmpl(SpaceMembershipExpiredSubject, SpaceMembershipExpiredMessage),
+		ShareCreated:           notiTmpl(ShareCreatedSubject, ShareCreatedMessage),
+		ShareRemoved:           notiTmpl(ShareRemovedSubject, ShareRemovedMessage),
+		ShareExpired:           notiTmpl(ShareExpiredSubject, ShareExpiredMessage),
+	}
+)
+
+// NotificationTemplate is the data structure for the notifications
+type NotificationTemplate struct {
+	Subject *template.Template
+	Message *template.Template
+}
+
+func notiTmpl(subjectname string, messagename string) NotificationTemplate {
+	return NotificationTemplate{
+		Subject: template.Must(template.New("").Parse(subjectname)),
+		Message: template.Must(template.New("").Parse(messagename)),
+	}
+}
diff --git a/services/web/Makefile b/services/web/Makefile
index 0dd380b8609..ffec79a74c6 100644
--- a/services/web/Makefile
+++ b/services/web/Makefile
@@ -1,6 +1,6 @@
 SHELL := bash
 NAME := web
-WEB_ASSETS_VERSION = v7.0.0-rc.18
+WEB_ASSETS_VERSION = v7.0.0-rc.20
 
 include ../../.make/recursion.mk
 


Test patch:
diff --git a/services/graph/pkg/identity/ldap_education_school_test.go b/services/graph/pkg/identity/ldap_education_school_test.go
index a97d4804d83..ffcbd918a78 100644
--- a/services/graph/pkg/identity/ldap_education_school_test.go
+++ b/services/graph/pkg/identity/ldap_education_school_test.go
@@ -322,7 +322,7 @@ var userByIDSearch1 *ldap.SearchRequest = &ldap.SearchRequest{
 	Scope:      2,
 	SizeLimit:  1,
 	Filter:     "(&(objectClass=ocEducationUser)(|(uid=abcd-defg)(entryUUID=abcd-defg)))",
-	Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+	Attributes: eduUserAttrs,
 	Controls:   []ldap.Control(nil),
 }
 
@@ -331,7 +331,7 @@ var userByIDSearch2 *ldap.SearchRequest = &ldap.SearchRequest{
 	Scope:      2,
 	SizeLimit:  1,
 	Filter:     "(&(objectClass=ocEducationUser)(|(uid=does-not-exist)(entryUUID=does-not-exist)))",
-	Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+	Attributes: eduUserAttrs,
 	Controls:   []ldap.Control(nil),
 }
 
@@ -439,7 +439,7 @@ var usersBySchoolIDSearch *ldap.SearchRequest = &ldap.SearchRequest{
 	Scope:      2,
 	SizeLimit:  0,
 	Filter:     "(&(objectClass=ocEducationUser)(ocMemberOfSchool=abcd-defg))",
-	Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+	Attributes: eduUserAttrs,
 	Controls:   []ldap.Control(nil),
 }
 
diff --git a/services/graph/pkg/identity/ldap_education_user_test.go b/services/graph/pkg/identity/ldap_education_user_test.go
index 960bec73be1..dc4777e4d0e 100644
--- a/services/graph/pkg/identity/ldap_education_user_test.go
+++ b/services/graph/pkg/identity/ldap_education_user_test.go
@@ -11,6 +11,8 @@ import (
 	"github.com/test-go/testify/mock"
 )
 
+var eduUserAttrs = []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute", "oCExternalIdentity", "userClass", "ocMemberOfSchool"}
+
 var eduUserEntry = ldap.NewEntry("uid=user,ou=people,dc=test",
 	map[string][]string{
 		"uid":         {"testuser"},
@@ -23,6 +25,18 @@ var eduUserEntry = ldap.NewEntry("uid=user,ou=people,dc=test",
 			"xxx $ http://idpnew $ xxxxx-xxxxx-xxxxx",
 		},
 	})
+var renamedEduUserEntry = ldap.NewEntry("uid=newtestuser,ou=people,dc=test",
+	map[string][]string{
+		"uid":         {"newtestuser"},
+		"displayname": {"Test User"},
+		"mail":        {"user@example"},
+		"entryuuid":   {"abcd-defg"},
+		"userClass":   {"student"},
+		"oCExternalIdentity": {
+			"$ http://idp $ testuser",
+			"xxx $ http://idpnew $ xxxxx-xxxxx-xxxxx",
+		},
+	})
 var eduUserEntryWithSchool = ldap.NewEntry("uid=user,ou=people,dc=test",
 	map[string][]string{
 		"uid":              {"testuser"},
@@ -42,7 +56,7 @@ var sr1 *ldap.SearchRequest = &ldap.SearchRequest{
 	Scope:      2,
 	SizeLimit:  1,
 	Filter:     "(&(objectClass=ocEducationUser)(|(uid=abcd-defg)(entryUUID=abcd-defg)))",
-	Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+	Attributes: eduUserAttrs,
 	Controls:   []ldap.Control(nil),
 }
 var sr2 *ldap.SearchRequest = &ldap.SearchRequest{
@@ -50,7 +64,7 @@ var sr2 *ldap.SearchRequest = &ldap.SearchRequest{
 	Scope:      2,
 	SizeLimit:  1,
 	Filter:     "(&(objectClass=ocEducationUser)(|(uid=xxxx-xxxx)(entryUUID=xxxx-xxxx)))",
-	Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+	Attributes: eduUserAttrs,
 	Controls:   []ldap.Control(nil),
 }
 
@@ -133,7 +147,7 @@ func TestGetEducationUsers(t *testing.T) {
 		Scope:      2,
 		SizeLimit:  0,
 		Filter:     "(objectClass=ocEducationUser)",
-		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass", "ocMemberOfSchool"},
+		Attributes: eduUserAttrs,
 		Controls:   []ldap.Control(nil),
 	}
 	lm.On("Search", sr).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil)
@@ -143,3 +157,96 @@ func TestGetEducationUsers(t *testing.T) {
 	lm.AssertNumberOfCalls(t, "Search", 1)
 	assert.Nil(t, err)
 }
+
+func TestUpdateEducationUser(t *testing.T) {
+	lm := &mocks.Client{}
+	b, err := getMockedBackend(lm, eduConfig, &logger)
+	assert.Nil(t, err)
+	userSearchReq := &ldap.SearchRequest{
+		BaseDN:     "ou=people,dc=test",
+		Scope:      2,
+		SizeLimit:  1,
+		Filter:     "(&(objectClass=ocEducationUser)(|(uid=testuser)(entryUUID=testuser)))",
+		Attributes: eduUserAttrs,
+	}
+	userLookupReq := &ldap.SearchRequest{
+		BaseDN:     "uid=newtestuser,ou=people,dc=test",
+		Scope:      0,
+		SizeLimit:  1,
+		Filter:     "(objectClass=inetOrgPerson)",
+		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname", "userEnabledAttribute"},
+	}
+	eduUserLookupReq := &ldap.SearchRequest{
+		BaseDN:     "uid=newtestuser,ou=people,dc=test",
+		Scope:      0,
+		SizeLimit:  1,
+		Filter:     "(objectClass=ocEducationUser)",
+		Attributes: eduUserAttrs,
+	}
+	groupSearchReq := &ldap.SearchRequest{
+		BaseDN: "ou=groups,dc=test",
+		Scope:  2,
+		Filter: "(&(objectClass=groupOfNames)(member=uid=user,ou=people,dc=test))",
+		Attributes: []string{
+			"cn",
+			"entryUUID",
+		},
+	}
+	lm.On("Search", userLookupReq).
+		Return(
+			&ldap.SearchResult{
+				Entries: []*ldap.Entry{
+					renamedEduUserEntry,
+				},
+			},
+			nil)
+	lm.On("Search", eduUserLookupReq).
+		Return(
+			&ldap.SearchResult{
+				Entries: []*ldap.Entry{
+					renamedEduUserEntry,
+				},
+			},
+			nil)
+	lm.On("Search", userSearchReq).
+		Return(
+			&ldap.SearchResult{
+				Entries: []*ldap.Entry{
+					eduUserEntry,
+				},
+			},
+			nil)
+	lm.On("Search", groupSearchReq).
+		Return(
+			&ldap.SearchResult{
+				Entries: []*ldap.Entry{},
+			},
+			nil)
+	modReq := ldap.ModifyRequest{
+		DN: "uid=newtestuser,ou=people,dc=test",
+		Changes: []ldap.Change{
+			{
+				Operation: ldap.ReplaceAttribute,
+				Modification: ldap.PartialAttribute{
+					Type: "mail",
+					Vals: []string{"new@mail.org"},
+				},
+			},
+		},
+	}
+	modDNReq := ldap.ModifyDNRequest{
+		DN:           "uid=user,ou=people,dc=test",
+		NewRDN:       "uid=newtestuser",
+		DeleteOldRDN: true,
+	}
+	lm.On("ModifyDN", &modDNReq).Return(nil)
+	lm.On("Modify", &modReq).Return(nil)
+	user := libregraph.NewEducationUser()
+	user.SetOnPremisesSamAccountName("newtestuser")
+	user.SetMail("new@mail.org")
+	eduUser, err := b.UpdateEducationUser(context.Background(), "testuser", *user)
+	assert.NotNil(t, eduUser)
+	assert.Nil(t, err)
+	assert.Equal(t, eduUser.GetOnPremisesSamAccountName(), "newtestuser")
+	assert.Equal(t, "abcd-defg", eduUser.GetId())
+}
diff --git a/services/userlog/pkg/service/service_test.go b/services/userlog/pkg/service/service_test.go
index 724d4c7d92a..89ce6871fa2 100644
--- a/services/userlog/pkg/service/service_test.go
+++ b/services/userlog/pkg/service/service_test.go
@@ -64,7 +64,7 @@ var _ = Describe("UserlogService", func() {
 			service.GatewayClient(&gwc),
 			service.HistoryClient(&ehc),
 			service.RegisteredEvents([]events.Unmarshaller{
-				events.UploadReady{},
+				events.SpaceDisabled{},
 			}),
 		)
 		Expect(err).ToNot(HaveOccurred())
@@ -73,9 +73,11 @@ var _ = Describe("UserlogService", func() {
 
 	It("it stores, returns and deletes a couple of events", func() {
 		ids := make(map[string]struct{})
-		ids[bus.Publish(events.SpaceCreated{Executant: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
-		ids[bus.Publish(events.UploadReady{SpaceOwner: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
-		ids[bus.Publish(events.ContainerCreated{SpaceOwner: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
+		ids[bus.Publish(events.SpaceDisabled{Executant: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
+		ids[bus.Publish(events.SpaceDisabled{Executant: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
+		ids[bus.Publish(events.SpaceDisabled{Executant: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
+		// ids[bus.Publish(events.SpaceMembershipExpired{SpaceOwner: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
+		// ids[bus.Publish(events.ShareCreated{Executant: &user.UserId{OpaqueId: "userid"}})] = struct{}{}
 
 		time.Sleep(500 * time.Millisecond)
 
diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php
index 00c92f6bc18..16e8c6f1e8d 100644
--- a/tests/TestHelpers/GraphHelper.php
+++ b/tests/TestHelpers/GraphHelper.php
@@ -700,20 +700,23 @@ class GraphHelper {
 		?bool $accountEnabled
 	): string {
 		$payload = [];
-		if ($userName) {
+		if ($userName !== null) {
+			// comment on after fixing #5755 because now it crashes server
+			// if (empty($userName)) {
+			//   $payload['onPremisesSamAccountName'] = ' ';
+			// } else $payload['onPremisesSamAccountName'] = $userName;
 			$payload['onPremisesSamAccountName'] = $userName;
 		}
-		if ($password) {
+		if ($password !== null) {
 			$payload['passwordProfile'] = ['password' => $password];
 		}
-		if ($displayName) {
+		if ($displayName !== null) {
 			$payload['displayName'] = $displayName;
 		}
-		if ($email) {
+		if ($email !== null) {
 			$payload['mail'] = $email;
 		}
 		$payload['accountEnabled'] = $accountEnabled;
-
 		return \json_encode($payload);
 	}
 
diff --git a/tests/TestHelpers/HttpRequestHelper.php b/tests/TestHelpers/HttpRequestHelper.php
index 828314681cc..caef4e44415 100644
--- a/tests/TestHelpers/HttpRequestHelper.php
+++ b/tests/TestHelpers/HttpRequestHelper.php
@@ -76,12 +76,9 @@ class HttpRequestHelper {
 	 * @return int
 	 */
 	public static function numRetriesOnHttpTooEarly():int {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently reva and oCIS may return HTTP_TOO_EARLY
-			// So try up to 10 times before giving up.
-			return 10;
-		}
-		return 0;
+		// Currently reva and oCIS may return HTTP_TOO_EARLY
+		// So try up to 10 times before giving up.
+		return 10;
 	}
 
 	/**
diff --git a/tests/TestHelpers/LoggingHelper.php b/tests/TestHelpers/LoggingHelper.php
index 574527ef8ca..33f52e02f38 100644
--- a/tests/TestHelpers/LoggingHelper.php
+++ b/tests/TestHelpers/LoggingHelper.php
@@ -53,32 +53,8 @@ class LoggingHelper {
 	public static function getLogFilePath(
 		?string $xRequestId = ''
 	):string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			return "";
-		}
-		$result = SetupHelper::runOcc(
-			['log:owncloud'],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not get owncloud log file information" .
-				$result ["stdOut"] . " " . $result ["stdErr"]
-			);
-		}
-		\preg_match(
-			"/Log backend ownCloud: (\w+)\sLog file: (.*)/",
-			$result ['stdOut'],
-			$matches
-		);
-		if (!isset($matches[1]) || $matches[1] !== "enabled") {
-			throw new Exception("log backend is not set to 'owncloud'");
-		}
-		if (!isset($matches[2])) {
-			throw new Exception("could not get owncloud log file information");
-		}
-		return $matches[2];
+		// Currently we don't interact with the log file on reva or OCIS
+		return "";
 	}
 
 	/**
@@ -132,23 +108,7 @@ class LoggingHelper {
 	public static function getLogLevel(
 		?string $xRequestId = ''
 	):string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return "debug";
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not get log level " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
-		if (!\preg_match("/Log level:\s(\w+)\s\(/", $result["stdOut"], $matches)) {
-			throw new Exception("could not get log level");
-		}
-		return \strtolower($matches[1]);
+		return "debug";
 	}
 
 	/**
@@ -163,23 +123,8 @@ class LoggingHelper {
 		?string $logLevel,
 		?string $xRequestId = ''
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we can't manage log file settings on reva or OCIS
-			return;
-		}
-		if (!\in_array($logLevel, self::LOG_LEVEL_ARRAY)) {
-			throw new InvalidArgumentException("invalid log level");
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage", "--level=$logLevel"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not set log level " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
+		// Currently we can't manage log file settings on reva or OCIS
+		return;
 	}
 
 	/**
@@ -193,28 +138,7 @@ class LoggingHelper {
 	public static function getLogBackend(
 		?string $xRequestId = ''
 	):string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return "errorlog";
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not get log backend " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
-		$pregResult = \preg_match(
-			"/Enabled logging backend:\s(\w+)\n/",
-			$result ["stdOut"],
-			$matches
-		);
-		if (!$pregResult) {
-			throw new Exception("could not get log backend");
-		}
-		return \strtolower($matches[1]);
+		return "errorlog";
 	}
 
 	/**
@@ -232,20 +156,8 @@ class LoggingHelper {
 		if (!\in_array($backend, ["owncloud", "syslog", "errorlog"])) {
 			throw new InvalidArgumentException("invalid log backend");
 		}
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we can't manage log file settings on reva or OCIS
-			return;
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage", "--backend=$backend"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not set log backend " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
+		// Currently we can't manage log file settings on reva or OCIS
+		return;
 	}
 
 	/**
@@ -259,28 +171,7 @@ class LoggingHelper {
 	public static function getLogTimezone(
 		?string  $xRequestId = ''
 	):string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return "UTC";
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not get log timezone " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
-		$pregResult = \preg_match(
-			"/Log timezone:\s(\w+)/",
-			$result ["stdOut"],
-			$matches
-		);
-		if (!$pregResult) {
-			throw new Exception("could not get log timezone");
-		}
-		return $matches[1];
+		return "UTC";
 	}
 
 	/**
@@ -295,20 +186,8 @@ class LoggingHelper {
 		?string $timezone,
 		?string $xRequestId = ''
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we can't manage log file settings on reva or OCIS
-			return;
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage", "--timezone=$timezone"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not set log timezone " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
+		// Currently we can't manage log file settings on reva or OCIS
+		return;
 	}
 
 	/**
@@ -327,21 +206,8 @@ class LoggingHelper {
 		?string $adminPassword,
 		?string $xRequestId = ''
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			return;
-		}
-		$result = OcsApiHelper::sendRequest(
-			$baseUrl,
-			$adminUsername,
-			$adminPassword,
-			"DELETE",
-			"/apps/testing/api/v1/logfile",
-			$xRequestId
-		);
-		if ($result->getStatusCode() !== 200) {
-			throw new Exception("could not clear logfile");
-		}
+		// Currently we don't interact with the log file on reva or OCIS
+		return;
 	}
 
 	/**
@@ -360,41 +226,8 @@ class LoggingHelper {
 		?string $timezone,
 		?string $xRequestId = ''
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			return;
-		}
-		if (!\in_array(\strtolower($logLevel), self::LOG_LEVEL_ARRAY)) {
-			throw new InvalidArgumentException("invalid log level");
-		}
-		if (!\in_array(\strtolower($backend), ["owncloud", "syslog", "errorlog"])) {
-			throw new InvalidArgumentException("invalid log backend");
-		}
-
-		$commands = ["log:manage"];
-
-		if ($timezone) {
-			\array_push($commands, "--timezone=$timezone");
-		}
-		if ($logLevel) {
-			\array_push($commands, "--backend=$backend");
-		}
-		if ($backend) {
-			\array_push($commands, "--level=$logLevel");
-		}
-
-		if (\count($commands) > 1) {
-			$result = SetupHelper::runOcc(
-				$commands,
-				$xRequestId
-			);
-			if ($result["code"] != 0) {
-				throw new Exception(
-					"could not restore log status " . $result ["stdOut"] . " " .
-					$result ["stdErr"]
-				);
-			}
-		}
+		// Currently we don't interact with the log file on reva or OCIS
+		return;
 	}
 
 	/**
@@ -408,50 +241,10 @@ class LoggingHelper {
 	public static function getLogInfo(
 		?string $xRequestId = ''
 	):array {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return [
-				"level" => "debug",
-				"backend" => "errorlog",
-				"timezone" => "UTC"
-			];
-		}
-		$result = SetupHelper::runOcc(
-			["log:manage"],
-			$xRequestId
-		);
-		if ($result["code"] != 0) {
-			throw new Exception(
-				"could not get log level " . $result ["stdOut"] . " " .
-				$result ["stdErr"]
-			);
-		}
-
-		$logging = [];
-		if (!\preg_match("/Log level:\s(\w+)\s\(/", $result["stdOut"], $matches)) {
-			throw new Exception("could not get log level");
-		}
-		$logging["level"] = $matches[1];
-
-		$pregResult = \preg_match(
-			"/Log timezone:\s(\w+)/",
-			$result ["stdOut"],
-			$matches
-		);
-		if (!$pregResult) {
-			throw new Exception("could not get log timezone");
-		}
-		$logging["timezone"] = $matches[1];
-
-		$pregResult = \preg_match(
-			"/Enabled logging backend:\s(\w+)\n/",
-			$result ["stdOut"],
-			$matches
-		);
-		if (!$pregResult) {
-			throw new Exception("could not get log backend");
-		}
-		$logging["backend"] = $matches[1];
-
-		return $logging;
+		return [
+			"level" => "debug",
+			"backend" => "errorlog",
+			"timezone" => "UTC"
+		];
 	}
 }
diff --git a/tests/TestHelpers/OcisHelper.php b/tests/TestHelpers/OcisHelper.php
index e8627babaef..38f13ab5aec 100644
--- a/tests/TestHelpers/OcisHelper.php
+++ b/tests/TestHelpers/OcisHelper.php
@@ -47,18 +47,11 @@ class OcisHelper {
 		return (\getenv("TEST_REVA") === "true");
 	}
 
-	/**
-	 * @return bool
-	 */
-	public static function isTestingOnOcisOrReva():bool {
-		return (self::isTestingOnOcis() || self::isTestingOnReva());
-	}
-
 	/**
 	 * @return bool
 	 */
 	public static function isTestingOnOc10():bool {
-		return (!self::isTestingOnOcisOrReva());
+		return false;
 	}
 
 	/**
@@ -307,7 +300,7 @@ class OcisHelper {
 	 */
 	private static function getOcisRevaDataRoot():string {
 		$root = \getenv("OCIS_REVA_DATA_ROOT");
-		if (($root === false || $root === "") && self::isTestingOnOcisOrReva()) {
+		if ($root === false || $root === "") {
 			$root = "/var/tmp/ocis/owncloud/";
 		}
 		if (!\file_exists($root)) {
diff --git a/tests/TestHelpers/SetupHelper.php b/tests/TestHelpers/SetupHelper.php
index 16e7d043ea3..0b6809dd5e5 100644
--- a/tests/TestHelpers/SetupHelper.php
+++ b/tests/TestHelpers/SetupHelper.php
@@ -789,54 +789,7 @@ class SetupHelper extends \PHPUnit\Framework\Assert {
 		?string $adminPassword = null,
 		?string $baseUrl = null
 	):array {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return [];
-		}
-		$baseUrl = self::checkBaseUrl($baseUrl, "runOcc");
-		$adminUsername = self::checkAdminUsername($adminUsername, "runOcc");
-		$adminPassword = self::checkAdminPassword($adminPassword, "runOcc");
-
-		if (!\is_array($commands)) {
-			throw new Exception("commands must be an array");
-		}
-
-		$isTestingAppEnabledText = "Is the testing app installed and enabled?\n";
-		$bodies = [];
-
-		foreach ($commands as $occ) {
-			if (!\array_key_exists('command', $occ)) {
-				throw new \InvalidArgumentException("command key is missing in array passed to runBulkOcc");
-			}
-
-			$body = [
-				'command' => \implode(' ', $occ['command'])
-			];
-
-			if (isset($occ['envVariables'])) {
-				$body['env_variables'] = $occ['envVariables'];
-			}
-			\array_push($bodies, $body);
-		}
-		try {
-			$result = OcsApiHelper::sendRequest(
-				$baseUrl,
-				$adminUsername,
-				$adminPassword,
-				"POST",
-				"/apps/testing/api/v1/occ/bulk?format=json",
-				$xRequestId,
-				\json_encode($bodies)
-			);
-		} catch (ServerException $e) {
-			throw new Exception(
-				"Could not execute 'occ'. " .
-				$isTestingAppEnabledText .
-				$e->getResponse()->getBody()
-			);
-		}
-		$result = \json_decode($result->getBody()->getContents());
-
-		return $result->ocs->data;
+		return [];
 	}
 
 	/**
@@ -864,129 +817,7 @@ class SetupHelper extends \PHPUnit\Framework\Assert {
 		?string $ocPath = null,
 		?array $envVariables = null
 	):array {
-		if (OcisHelper::isTestingOnOcisOrReva() && !OcisHelper::isTestingParallelDeployment()) {
-			return ['code' => '', 'stdOut' => '', 'stdErr' => '' ];
-		}
-		$baseUrl = self::checkBaseUrl($baseUrl, "runOcc");
-		$adminUsername = self::checkAdminUsername($adminUsername, "runOcc");
-		$adminPassword = self::checkAdminPassword($adminPassword, "runOcc");
-		if (self::$ocPath === null
-			&& $ocPath === null
-		) {
-			throw new Exception(
-				"runOcc called without ocPath - pass the ocPath or call SetupHelper::init"
-			);
-		}
-		if ($ocPath === null) {
-			$ocPath = self::$ocPath;
-		} else {
-			$ocPath = self::normaliseOcPath($ocPath);
-		}
-
-		$body = [];
-		$argsString = \implode(' ', $args);
-		$body['command'] = $argsString;
-
-		if ($envVariables !== null) {
-			$body['env_variables'] = $envVariables;
-		}
-
-		$isTestingAppEnabledText = "Is the testing app installed and enabled?\n";
-
-		try {
-			$result = OcsApiHelper::sendRequest(
-				$baseUrl,
-				$adminUsername,
-				$adminPassword,
-				"POST",
-				$ocPath,
-				$xRequestId,
-				$body
-			);
-		} catch (ServerException $e) {
-			throw new Exception(
-				"Could not execute 'occ'. " .
-				$isTestingAppEnabledText .
-				$e->getResponse()->getBody()
-			);
-		}
-
-		$return = [];
-		$contents = $result->getBody()->getContents();
-		$resultXml = \simplexml_load_string($contents);
-
-		if ($resultXml === false) {
-			$status = $result->getStatusCode();
-			throw new Exception(
-				"Response is not valid XML after executing 'occ $argsString'. " .
-				"HTTP status was $status. " .
-				$isTestingAppEnabledText .
-				"Response contents were '$contents'"
-			);
-		}
-
-		$return['code'] = $resultXml->xpath("//ocs/data/code");
-		$return['stdOut'] = $resultXml->xpath("//ocs/data/stdOut");
-		$return['stdErr'] = $resultXml->xpath("//ocs/data/stdErr");
-
-		if (!isset($return['code'][0])) {
-			throw new Exception(
-				"Return code not found after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		if (!isset($return['stdOut'][0])) {
-			throw new Exception(
-				"Return stdOut not found after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		if (!isset($return['stdErr'][0])) {
-			throw new Exception(
-				"Return stdErr not found after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		if (!\is_a($return['code'][0], "SimpleXMLElement")) {
-			throw new Exception(
-				"Return code is not a SimpleXMLElement after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		if (!\is_a($return['stdOut'][0], "SimpleXMLElement")) {
-			throw new Exception(
-				"Return stdOut is not a SimpleXMLElement after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		if (!\is_a($return['stdErr'][0], "SimpleXMLElement")) {
-			throw new Exception(
-				"Return stdErr is not a SimpleXMLElement after executing 'occ $argsString'. " .
-				$isTestingAppEnabledText .
-				$contents
-			);
-		}
-
-		$return['code'] = $return['code'][0]->__toString();
-		$return['stdOut'] = $return['stdOut'][0]->__toString();
-		$return['stdErr'] = $return['stdErr'][0]->__toString();
-		self::resetOpcache(
-			$baseUrl,
-			$adminUsername,
-			$adminPassword,
-			$xRequestId
-		);
-		return $return;
+		return ['code' => '', 'stdOut' => '', 'stdErr' => '' ];
 	}
 
 	/**
diff --git a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
index dbc424c3783..c35cff60f80 100644
--- a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
+++ b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
@@ -104,8 +104,12 @@ The expected failures in this file are from features in the owncloud/ocis repo.
 #### [Normal user can get expanded members information of a group](https://github.com/owncloud/ocis/issues/5604)
 - [apiGraph/getGroup.feature:100](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/getGroup.feature#L100)
 
-#### Change editUser test where we set empty value
-- [apiGraph/editUser.feature:33](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/editUser.feature#L33)
-- [apiGraph/editUser.feature:80](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/editUser.feature#L80)
+#### [Changing user with an uppercase name gives 404 error](https://github.com/owncloud/ocis/issues/5763)
+- [apiGraph/editUser.feature:41](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/editUser.feature#L41)
+
+#### [Using # in the onPremisesSamAccountName breaks getting users](https://github.com/owncloud/ocis/issues/5755)
+- [apiGraph/editUser.feature:42](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/editUser.feature#L42)
+- [apiGraph/editUser.feature:44](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/editUser.feature#L44)
+
 Note: always have an empty line at the end of this file.
 The bash script that processes this file requires that the last line has a newline on the end.
diff --git a/tests/acceptance/features/apiGraph/addUserToGroup.feature b/tests/acceptance/features/apiGraph/addUserToGroup.feature
index d9a48508272..03e799358dd 100644
--- a/tests/acceptance/features/apiGraph/addUserToGroup.feature
+++ b/tests/acceptance/features/apiGraph/addUserToGroup.feature
@@ -136,7 +136,7 @@ Feature: add users to group
 
 
   Scenario: admin tries to add user to a non-existing group
-    When the administrator tries to add user "Alice" to group "nonexistentgroup" using the Graph API
+    When the administrator tries to add user "Alice" to a nonexistent group using the Graph API
     Then the HTTP status code should be "404"
 
 
@@ -147,7 +147,7 @@ Feature: add users to group
 
 
   Scenario: admin tries to add user to a group without sending the group
-    When the administrator tries to add user "Alice" to group "" using the Graph API
+    When the administrator tries to add user "Alice" to a nonexistent group using the Graph API
     Then the HTTP status code should be "404"
 
 
@@ -175,7 +175,7 @@ Feature: add users to group
       | username |
       | Brian    |
       | Carol    |
-    When the administrator "Alice" tries to add the following users to a non-existing group at once using the Graph API
+    When the administrator "Alice" tries to add the following users to a nonexistent group at once using the Graph API
       | username |
       | Brian    |
       | Carol    |
@@ -185,7 +185,7 @@ Feature: add users to group
   Scenario: admin tries to add multiple non-existing users to a group at once
     Given the administrator has given "Alice" the role "Admin" using the settings api
     And user "Alice" has created a group "grp1" using the Graph API
-    When the administrator "Alice" tries to add the following non-existing users to a group "grp1" at once using the Graph API
+    When the administrator "Alice" tries to add the following nonexistent users to a group "grp1" at once using the Graph API
       | username |
       | Brian    |
       | Carol    |
@@ -198,7 +198,7 @@ Feature: add users to group
       | username |
       | Brian    |
     And user "Alice" has created a group "grp1" using the Graph API
-    When the administrator "Alice" tries to add the following users to a group "grp1" at once using the Graph API
+    When the administrator "Alice" tries to add the following existent and nonexistent users to a group "grp1" at once using the Graph API
       | username |
       | Brian    |
       | Carol    |
diff --git a/tests/acceptance/features/apiGraph/editUser.feature b/tests/acceptance/features/apiGraph/editUser.feature
index dd62d374fcf..06a8476f336 100644
--- a/tests/acceptance/features/apiGraph/editUser.feature
+++ b/tests/acceptance/features/apiGraph/editUser.feature
@@ -14,28 +14,62 @@ Feature: edit user
       | password    | 1234              |
 
 
-  Scenario: the admin user can edit another user email
-    When the user "Alice" changes the email of user "Brian" to "newemail@example.com" using the Graph API
-    Then the HTTP status code should be "200"
+  Scenario Outline: the admin user can edit another user's email
+    When the user "Alice" changes the email of user "Brian" to "<newEmail>" using the Graph API
+    Then the HTTP status code should be "<code>"
     And the user "Brian" should have information with these key and value pairs:
-      | key  | value                |
-      | mail | newemail@example.com |
+      | key  | value           |
+      | mail | <emailAsResult> |
+    Examples:
+      | action description        | newEmail             | code | emailAsResult        |
+      | change to a valid email   | newemail@example.com | 200  | newemail@example.com |
+      | override existing mail    | brian@example.com    | 200  | brian@example.com    |
+      | two users with same mail  | alice@example.org    | 200  | alice@example.org    |
+      | empty mail                |                      | 400  | brian@example.com    |
+      | change to a invalid email | invalidEmail         | 400  | brian@example.com    |
 
 
-  Scenario: the admin user can override an existing user email of another user
-    When the user "Alice" changes the email of user "Brian" to "brian@example.com" using the Graph API
+  Scenario Outline: the admin user can edit another user's name
+    Given user "Carol" has been created with default attributes and without skeleton files
+    When the user "Alice" changes the user name of user "Carol" to "<userName>" using the Graph API
+    Then the HTTP status code should be "<code>"
+    And the user "<userNameAsResult>" should have information with these key and value pairs:
+      | key                      | value              |
+      | onPremisesSamAccountName | <userNameAsResult> |
+    Examples:
+      | action description           | userName | code | userNameAsResult |
+      | change to a valid user name  | Lionel   | 200  | Lionel           |
+      | user name characters         | *:!;_+-& | 200  | *:!;_+-&         |
+      | change to existing user name | Brian    | 409  | Brian            |
+      | empty user name              |          | 400  | Brian            |
+
+
+  Scenario: the admin user changes the name of a user to the name of an existing disabled user
+    Given the user "Alice" has created a new user using the Graph API with the following settings:
+      | userName    | sam             |
+      | displayName | sam             |
+      | email       | sam@example.com |
+      | password    | 1234            |
+    And the user "Alice" has disabled user "Brian" using the Graph API
+    When the user "Alice" changes the user name of user "sam" to "Brian" using the Graph API
+    Then the HTTP status code should be "409"
+    And the user "sam" should have information with these key and value pairs:
+      | key                      | value |
+      | onPremisesSamAccountName | sam   |
+
+
+  Scenario: the admin user changes the name of a user to the name of a previously deleted user
+    Given the user "Alice" has created a new user using the Graph API with the following settings:
+      | userName    | sam             |
+      | displayName | sam             |
+      | email       | sam@example.com |
+      | password    | 1234            |
+    And the user "Alice" has deleted a user "sam" using the Graph API
+    When the user "Alice" changes the user name of user "Brian" to "sam" using the Graph API
     Then the HTTP status code should be "200"
-    And the user "Brian" should have information with these key and value pairs:
-      | key  | value             |
-      | mail | brian@example.com |
-
-
-  Scenario: the admin user cannot clear an existing user email
-    When the user "Alice" tries to change the email of user "Brian" to "" using the Graph API
-    Then the HTTP status code should be "400"
-    And the user "Brian" should have information with these key and value pairs:
-      | key  | value             |
-      | mail | brian@example.com |
+    And the user "sam" should have information with these key and value pairs:
+      | key                      | value |
+      | onPremisesSamAccountName | sam   |
 
 
   Scenario Outline: a normal user should not be able to change their email address
@@ -69,20 +103,18 @@ Feature: edit user
       | User        |
 
 
-  Scenario: the admin user can edit another user display name
-    When the user "Alice" changes the display name of user "Brian" to "Carol King" using the Graph API
+  Scenario Outline: the admin user can edit another user display name
+    When the user "Alice" changes the display name of user "Brian" to "<newDisplayName>" using the Graph API
     Then the HTTP status code should be "200"
     And the user "Brian" should have information with these key and value pairs:
-      | key         | value      |
-      | displayName | Carol King |
-
-
-  Scenario: the admin user cannot clear another user display name
-    When the user "Alice" tries to change the display name of user "Brian" to "" using the Graph API
-    Then the HTTP status code should be "400"
-    And the user "Brian" should have information with these key and value pairs:
-      | key         | value        |
-      | displayName | Brian Murphy |
+      | key         | value                 |
+      | displayName | <displayNameAsResult> |
+    Examples:
+      | action description                | newDisplayName | code | displayNameAsResult |
+      | change to a display name          | Olaf Scholz    | 200  | Olaf Scholz         |
+      | override to existing display name | Carol King     | 200  | Carol King          |
+      | change to an empty display name   |                | 400  | Brian Murphy        |
+      | displayName with characters       | *:!;_+-&#(?)   | 200  | *:!;_+-&#(?)        |
 
 
   Scenario Outline: a normal user should not be able to change his/her own display name
diff --git a/tests/acceptance/features/apiGraph/removeUserFromGroup.feature b/tests/acceptance/features/apiGraph/removeUserFromGroup.feature
index f2a51a62f65..554773c8c23 100644
--- a/tests/acceptance/features/apiGraph/removeUserFromGroup.feature
+++ b/tests/acceptance/features/apiGraph/removeUserFromGroup.feature
@@ -152,8 +152,8 @@ Feature: remove a user from a group
       | Alice    | var/../etc       |
 
 
-  Scenario: admin tries to remove a user from a non-existing group
-    When the administrator tries to remove user "Alice" from group "nonexistentgroup" using the Graph API
+  Scenario: admin tries to remove a user from a nonexistent group
+    When the administrator tries to remove user "Alice" from a nonexistent group using the Graph API
     Then the HTTP status code should be "404"
 
 
diff --git a/tests/acceptance/features/bootstrap/AppConfigurationContext.php b/tests/acceptance/features/bootstrap/AppConfigurationContext.php
index 90d954ebe54..580e3524427 100644
--- a/tests/acceptance/features/bootstrap/AppConfigurationContext.php
+++ b/tests/acceptance/features/bootstrap/AppConfigurationContext.php
@@ -73,12 +73,7 @@ class AppConfigurationContext implements Context {
 	public function serverParameterHasBeenSetTo(string $parameter, string $app, string $value):void {
 		// The capturing group of the regex always includes the quotes at each
 		// end of the captured string, so trim them.
-		if (\TestHelpers\OcisHelper::isTestingOnOcisOrReva()) {
-			return;
-		}
-		$value = \trim($value, $value[0]);
-		$this->modifyAppConfig($app, $parameter, $value);
-		$this->featureContext->clearStatusCodeArrays();
+		return;
 	}
 
 	/**
diff --git a/tests/acceptance/features/bootstrap/FeatureContext.php b/tests/acceptance/features/bootstrap/FeatureContext.php
index 532047a70ac..11d27f1f529 100644
--- a/tests/acceptance/features/bootstrap/FeatureContext.php
+++ b/tests/acceptance/features/bootstrap/FeatureContext.php
@@ -1593,11 +1593,7 @@ class FeatureContext extends BehatVariablesContext {
 			$urlEnding = \substr($url, \strlen($this->getBaseUrl() . '/'));
 		}
 
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$matchResult = \preg_match("%^(#/)?s/([a-zA-Z0-9]{15})$%", $urlEnding);
-		} else {
-			$matchResult = \preg_match("%^(index.php/)?s/([a-zA-Z0-9]{15})$%", $urlEnding);
-		}
+		$matchResult = \preg_match("%^(#/)?s/([a-zA-Z0-9]{15})$%", $urlEnding);
 
 		// preg_match returns (int) 1 for a match, we want to return a boolean.
 		if ($matchResult === 1) {
@@ -3915,24 +3911,6 @@ class FeatureContext extends BehatVariablesContext {
 		Assert::assertEquals("200", $response->getStatusCode());
 	}
 
-	/**
-	 * After Scenario. clear file locks
-	 *
-	 * @AfterScenario
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function clearFileLocks(): void {
-		if (!OcisHelper::isTestingOnOcisOrReva()) {
-			$this->authContext->deleteTokenAuthEnforcedAfterScenario();
-			$this->clearFileLocksForServer($this->getBaseUrl());
-			if ($this->remoteBaseUrl !== $this->localBaseUrl) {
-				$this->clearFileLocksForServer($this->getRemoteBaseUrl());
-			}
-		}
-	}
-
 	/**
 	 * @AfterScenario
 	 *
@@ -3956,72 +3934,7 @@ class FeatureContext extends BehatVariablesContext {
 	 * @throws Exception
 	 */
 	public static function useBigFileIDs(BeforeSuiteScope $scope): void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return;
-		}
-		$fullUrl = \getenv('TEST_SERVER_URL');
-		if (\substr($fullUrl, -1) !== '/') {
-			$fullUrl .= '/';
-		}
-		$fullUrl .= "ocs/v1.php/apps/testing/api/v1/increasefileid";
-		$suiteSettingsContexts = $scope->getSuite()->getSettings()['contexts'];
-		$adminUsername = null;
-		$adminPassword = null;
-		foreach ($suiteSettingsContexts as $context) {
-			if (isset($context[__CLASS__])) {
-				$adminUsername = $context[__CLASS__]['adminUsername'];
-				$adminPassword = $context[__CLASS__]['adminPassword'];
-				break;
-			}
-		}
-
-		// get the admin username from the environment (if defined)
-		$adminUsernameFromEnvironment = self::getAdminUsernameFromEnvironment();
-		if ($adminUsernameFromEnvironment !== false) {
-			$adminUsername = $adminUsernameFromEnvironment;
-		}
-
-		// get the admin password from the environment (if defined)
-		$adminPasswordFromEnvironment = self::getAdminPasswordFromEnvironment();
-		if ($adminPasswordFromEnvironment !== false) {
-			$adminPassword = $adminPasswordFromEnvironment;
-		}
-
-		if (($adminUsername === null) || ($adminPassword === null)) {
-			throw new Exception(
-				"Could not find adminUsername and/or adminPassword in useBigFileIDs"
-			);
-		}
-
-		HttpRequestHelper::post(
-			$fullUrl,
-			'',
-			$adminUsername,
-			$adminPassword
-		);
-	}
-
-	/**
-	 * runs a function on every server (LOCAL & REMOTE).
-	 * The callable function receives the server (LOCAL or REMOTE) as first argument
-	 *
-	 * @param callable $callback
-	 *
-	 * @return array
-	 */
-	public function runFunctionOnEveryServer(callable $callback): array {
-		$previousServer = $this->getCurrentServer();
-		$result = [];
-		foreach (['LOCAL', 'REMOTE'] as $server) {
-			$this->usingServer($server);
-			if (($server === 'LOCAL')
-				|| $this->federatedServerExists()
-			) {
-				$result[$server] = \call_user_func($callback, $server);
-			}
-		}
-		$this->usingServer($previousServer);
-		return $result;
+		return;
 	}
 
 	/**
@@ -4349,25 +4262,6 @@ class FeatureContext extends BehatVariablesContext {
 		}
 	}
 
-	/**
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function restoreParametersAfterScenario(): void {
-		if (!OcisHelper::isTestingOnOcisOrReva()) {
-			$this->authContext->deleteTokenAuthEnforcedAfterScenario();
-			$user = $this->getCurrentUser();
-			$this->setCurrentUser($this->getAdminUsername());
-			$this->runFunctionOnEveryServer(
-				function ($server) {
-					$this->restoreParameters($server);
-				}
-			);
-			$this->setCurrentUser($user);
-		}
-	}
-
 	/**
 	 * Get the array of trusted servers in format ["url" => "id"]
 	 *
@@ -4440,37 +4334,6 @@ class FeatureContext extends BehatVariablesContext {
 		return $body;
 	}
 
-	/**
-	 * @BeforeScenario
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function prepareParametersBeforeScenario(): void {
-		if (!OcisHelper::isTestingOnOcisOrReva()) {
-			$user = $this->getCurrentUser();
-			$this->setCurrentUser($this->getAdminUsername());
-			$previousServer = $this->getCurrentServer();
-			foreach (['LOCAL', 'REMOTE'] as $server) {
-				if (($server === 'LOCAL') || $this->federatedServerExists()) {
-					$this->usingServer($server);
-					$this->resetAppConfigs();
-					$result = SetupHelper::runOcc(
-						['config:list', '--private'],
-						$this->getStepLineRef(),
-						$this->getAdminUsername(),
-						$this->getAdminPassword(),
-						$this->getBaseUrl(),
-						$this->getOcPath()
-					);
-					$this->savedConfigList[$server] = \json_decode($result['stdOut'], true);
-				}
-			}
-			$this->usingServer($previousServer);
-			$this->setCurrentUser($user);
-		}
-	}
-
 	/**
 	 * Before Scenario to Save trusted Servers
 	 *
@@ -4486,85 +4349,6 @@ class FeatureContext extends BehatVariablesContext {
 		];
 	}
 
-	/**
-	 * restore settings of the system and delete new settings that were created in the test runs
-	 *
-	 * @param string $server LOCAL|REMOTE
-	 *
-	 * @return void
-	 *
-	 * @throws Exception
-	 * @throws GuzzleException
-	 *
-	 */
-	private function restoreParameters(string $server): void {
-		$commands = [];
-		if ($this->isTestingWithLdap()) {
-			$this->resetOldLdapConfig();
-		}
-		$result = SetupHelper::runOcc(
-			['config:list'],
-			$this->getStepLineRef(),
-			$this->getAdminUsername(),
-			$this->getAdminPassword(),
-			$this->getBaseUrl(),
-			$this->getOcPath()
-		);
-		$currentConfigList = \json_decode($result['stdOut'], true);
-		foreach ($currentConfigList['system'] as $configKey => $configValue) {
-			if (!\array_key_exists(
-				$configKey,
-				$this->savedConfigList[$server]['system']
-			)
-			) {
-				$commands[] = ["command" => ['config:system:delete', $configKey]];
-			}
-		}
-		foreach ($this->savedConfigList[$server]['system'] as $configKey => $configValue) {
-			if (!\array_key_exists($configKey, $currentConfigList["system"])
-				|| $currentConfigList["system"][$configKey] !== $this->savedConfigList[$server]['system'][$configKey]
-			) {
-				$commands[] = ["command" => ['config:system:set', "--type=json", "--value=" . \json_encode($configValue), $configKey]];
-			}
-		}
-		foreach ($currentConfigList['apps'] as $appName => $appSettings) {
-			foreach ($appSettings as $configKey => $configValue) {
-				//only check if the app was there in the original configuration
-				if (\array_key_exists($appName, $this->savedConfigList[$server]['apps'])
-					&& !\array_key_exists(
-						$configKey,
-						$this->savedConfigList[$server]['apps'][$appName]
-					)
-				) {
-					$commands[] = ["command" => ['config:app:delete', $appName, $configKey]];
-				} elseif (\array_key_exists($appName, $this->savedConfigList[$server]['apps'])
-					&& \array_key_exists($configKey, $this->savedConfigList[$server]['apps'][$appName])
-					&& $this->savedConfigList[$server]['apps'][$appName][$configKey] !== $configValue
-				) {
-					// Do not accidentally disable apps here (perhaps too early)
-					// That is done in Provisioning.php restoreAppEnabledDisabledState()
-					if ($configKey !== "enabled") {
-						$commands[] = [
-							"command" => [
-								'config:app:set',
-								$appName,
-								$configKey,
-								"--value=" . $this->savedConfigList[$server]['apps'][$appName][$configKey]
-							]
-						];
-					}
-				}
-			}
-		}
-		SetupHelper::runBulkOcc(
-			$commands,
-			$this->getStepLineRef(),
-			$this->getAdminUsername(),
-			$this->getAdminPassword(),
-			$this->getBaseUrl()
-		);
-	}
-
 	/**
 	 * The method returns userId
 	 *
@@ -4600,7 +4384,7 @@ class FeatureContext extends BehatVariablesContext {
 	 * @return string
 	 * @throws Exception|GuzzleException
 	 */
-	public function getGroupIdByGroupName(string $groupName): string {
+	public function getGroupIdByGroupName(string $groupName):string {
 		$response = GraphHelper::getGroup(
 			$this->getBaseUrl(),
 			$this->getStepLineRef(),
diff --git a/tests/acceptance/features/bootstrap/GraphContext.php b/tests/acceptance/features/bootstrap/GraphContext.php
index 1c552f0a1d6..8275c468a9a 100644
--- a/tests/acceptance/features/bootstrap/GraphContext.php
+++ b/tests/acceptance/features/bootstrap/GraphContext.php
@@ -128,6 +128,27 @@ class GraphContext implements Context {
 		$this->featureContext->setResponse($response);
 	}
 
+	/**
+	 * @When /^the user "([^"]*)" changes the user name of user "([^"]*)" to "([^"]*)" using the Graph API$/
+	 * @When /^the user "([^"]*)" tries to change the user name of user "([^"]*)" to "([^"]*)" using the Graph API$/
+	 *
+	 * @param string $byUser
+	 * @param string $user
+	 * @param string $userName
+	 *
+	 * @return void
+	 * @throws GuzzleException
+	 * @throws Exception
+	 */
+	public function theUserChangesTheUserNameOfUserToUsingTheGraphApi(string $byUser, string $user, string $userName): void {
+		$response = $this->editUserUsingTheGraphApi($byUser, $user, $userName);
+		$this->featureContext->setResponse($response);
+		// need add user to list to delete him after test
+		if (!empty($userName)) {
+			$this->featureContext->addUserToCreatedUsersList($userName, $this->featureContext->getUserPassword($user));
+		}
+	}
+
 	/**
 	 * @When /^the user "([^"]*)" disables user "([^"]*)" using the Graph API$/
 	 * @When /^the user "([^"]*)" tries to disable user "([^"]*)" using the Graph API$/
@@ -241,12 +262,8 @@ class GraphContext implements Context {
 	 */
 	public function adminHasRetrievedUserUsingTheGraphApi(string $user): void {
 		$user = $this->featureContext->getActualUsername($user);
-		try {
-			$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
-			$userId = $userId ?? $user;
-		} catch (Exception $e) {
-			$userId = $user;
-		}
+		$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
+		$userId = $userId ? $userId : $user;
 		$result = GraphHelper::getUser(
 			$this->featureContext->getBaseUrl(),
 			$this->featureContext->getStepLineRef(),
@@ -816,17 +833,8 @@ class GraphContext implements Context {
 	 */
 	public function addUserToGroup(string $group, string $user, ?string $byUser = null): ResponseInterface {
 		$credentials = $this->getAdminOrUserCredentials($byUser);
-		try {
-			$groupId = $this->featureContext->getAttributeOfCreatedGroup($group, "id");
-		} catch (Exception $e) {
-			$groupId = WebDavHelper::generateUUIDv4();
-		}
-		try {
-			$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
-		} catch (Exception $e) {
-			$userId = WebDavHelper::generateUUIDv4();
-		}
-
+		$groupId = $this->featureContext->getAttributeOfCreatedGroup($group, "id");
+		$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
 		return GraphHelper::addUserToGroup(
 			$this->featureContext->getBaseUrl(),
 			$this->featureContext->getStepLineRef(),
@@ -888,6 +896,33 @@ class GraphContext implements Context {
 		$this->featureContext->setResponse($this->addUserToGroup($group, $user));
 	}
 
+	/**
+	 * @When the administrator tries to add user :user to a nonexistent group using the Graph API
+	 * @When the user :byUser tries to add user :user to a nonexistent group using the Graph API
+	 *
+	 * @param string $user
+	 * @param string|null $byUser
+	 *
+	 * @return void
+	 *
+	 * @throws GuzzleException | Exception
+	 */
+	public function theAdministratorTriesToAddUserToNonExistentGroupUsingTheGraphAPI(string $user, ?string $byUser = null): void {
+		$credentials = $this->getAdminOrUserCredentials($byUser);
+		$groupId = WebDavHelper::generateUUIDv4();
+		$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
+		$this->featureContext->setResponse(
+			GraphHelper::addUserToGroup(
+				$this->featureContext->getBaseUrl(),
+				$this->featureContext->getStepLineRef(),
+				$credentials['username'],
+				$credentials['password'],
+				$userId,
+				$groupId
+			)
+		);
+	}
+
 	/**
 	 * @When user :user tries to add himself/herself to group :group using the Graph API
 	 *
@@ -1297,18 +1332,27 @@ class GraphContext implements Context {
 	 * @param string|null $byUser
 	 *
 	 * @return void
+	 * @throws Exception | GuzzleException
 	 */
 	public function theUserTriesToRemoveAnotherUserFromGroupUsingTheGraphAPI(string $user, string $group, ?string $byUser = null): void {
-		try {
-			$groupId = $this->featureContext->getAttributeOfCreatedGroup($group, "id");
-		} catch (Exception $e) {
-			$groupId = WebDavHelper::generateUUIDv4();
-		}
-		try {
-			$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
-		} catch (Exception $e) {
-			$userId = WebDavHelper::generateUUIDv4();
-		}
+		$groupId = $this->featureContext->getAttributeOfCreatedGroup($group, "id");
+		$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
+		$this->featureContext->setResponse($this->removeUserFromGroup($groupId, $userId, $byUser));
+	}
+
+	/**
+	 * @When the administrator tries to remove user :user from a nonexistent group using the Graph API
+	 * @When user :byUser tries to remove user :user from a nonexistent group using the Graph API
+	 *
+	 * @param string $user
+	 * @param string|null $byUser
+	 *
+	 * @return void
+	 * @throws GuzzleException
+	 */
+	public function theUserTriesToRemoveAnotherUserFromNonExistentGroupUsingTheGraphAPI(string $user, ?string $byUser = null): void {
+		$groupId = WebDavHelper::generateUUIDv4();
+		$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
 		$this->featureContext->setResponse($this->removeUserFromGroup($groupId, $userId, $byUser));
 	}
 
@@ -1680,7 +1724,7 @@ class GraphContext implements Context {
 	}
 
 	/**
-	 * @When /^the administrator "([^"]*)" tries to add the following users to a non-existing group at once using the Graph API$/
+	 * @When /^the administrator "([^"]*)" tries to add the following users to a nonexistent group at once using the Graph API$/
 	 *
 	 * @param string $user
 	 * @param TableNode $table
@@ -1699,7 +1743,7 @@ class GraphContext implements Context {
 	}
 
 	/**
-	 * @When /^the administrator "([^"]*)" tries to add the following non-existing users to a group "([^"]*)" at once using the Graph API$/
+	 * @When /^the administrator "([^"]*)" tries to add the following nonexistent users to a group "([^"]*)" at once using the Graph API$/
 	 *
 	 * @param string $user
 	 * @param string $group
@@ -1720,6 +1764,7 @@ class GraphContext implements Context {
 
 	/**
 	 * @When /^the administrator "([^"]*)" tries to add the following users to a group "([^"]*)" at once using the Graph API$/
+	 * @When /^the administrator "([^"]*)" tries to add the following existent and nonexistent users to a group "([^"]*)" at once using the Graph API$/
 	 *
 	 * @param string $user
 	 * @param string $group
@@ -1733,11 +1778,8 @@ class GraphContext implements Context {
 		$userIds = [];
 		$groupId = $this->featureContext->getAttributeOfCreatedGroup($group, "id");
 		foreach ($table->getHash() as $row) {
-			try {
-				$userIds[] = $this->featureContext->getAttributeOfCreatedUser($row['username'], "id");
-			} catch (Exception $e) {
-				$userIds[] = WebDavHelper::generateUUIDv4();
-			}
+			$userId = $this->featureContext->getAttributeOfCreatedUser($row['username'], "id");
+			$userIds[] = $userId ? $userId : WebDavHelper::generateUUIDv4();
 		}
 		$this->addMultipleUsersToGroup($user, $userIds, $groupId, $table);
 	}
diff --git a/tests/acceptance/features/bootstrap/LoggingContext.php b/tests/acceptance/features/bootstrap/LoggingContext.php
index c361da5202a..6cd49681b3b 100644
--- a/tests/acceptance/features/bootstrap/LoggingContext.php
+++ b/tests/acceptance/features/bootstrap/LoggingContext.php
@@ -65,86 +65,9 @@ class LoggingContext implements Context {
 		int       $ignoredLines = 0,
 		?TableNode $expectedLogEntries = null
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			// So skip processing this test step.
-			return;
-		}
-		$ignoredLines = (int) $ignoredLines;
-		//-1 because getRows gives also the header
-		$linesToRead = \count($expectedLogEntries->getRows()) - 1 + $ignoredLines;
-		$logLines = LoggingHelper::getLogFileContent(
-			$this->featureContext->getBaseUrl(),
-			$this->featureContext->getAdminUsername(),
-			$this->featureContext->getAdminPassword(),
-			$this->featureContext->getStepLineRef(),
-			$linesToRead
-		);
-		$lineNo = 0;
-		foreach ($expectedLogEntries as $expectedLogEntry) {
-			$logEntry = \json_decode($logLines[$lineNo], true);
-			if ($logEntry === null) {
-				throw new Exception("the log line :\n{$logLines[$lineNo]} is not valid JSON");
-			}
-
-			foreach (\array_keys($expectedLogEntry) as $attribute) {
-				if ($comparingMode === 'matching') {
-					$expectedLogEntry[$attribute]
-						= $this->featureContext->substituteInLineCodes(
-							$expectedLogEntry[$attribute],
-							null,
-							['preg_quote' => ['/']]
-						);
-				} else {
-					$expectedLogEntry[$attribute]
-						= $this->featureContext->substituteInLineCodes(
-							$expectedLogEntry[$attribute]
-						);
-				}
-
-				if ($expectedLogEntry[$attribute] !== "") {
-					Assert::assertArrayHasKey(
-						$attribute,
-						$logEntry,
-						"could not find attribute: '$attribute' in log entry: '{$logLines[$lineNo]}'"
-					);
-					$message = "log entry:\n{$logLines[$lineNo]}\n";
-					if (!\is_string($logEntry[$attribute])) {
-						$logEntry[$attribute] = \json_encode(
-							$logEntry[$attribute],
-							JSON_UNESCAPED_SLASHES
-						);
-					}
-					if ($comparingMode === 'with') {
-						Assert::assertEquals(
-							$expectedLogEntry[$attribute],
-							$logEntry[$attribute],
-							$message
-						);
-					} elseif ($comparingMode === 'containing') {
-						Assert::assertStringContainsString(
-							$expectedLogEntry[$attribute],
-							$logEntry[$attribute],
-							$message
-						);
-					} elseif ($comparingMode === 'matching') {
-						Assert::assertMatchesRegularExpression(
-							$expectedLogEntry[$attribute],
-							$logEntry[$attribute],
-							$message
-						);
-					} else {
-						throw new \InvalidArgumentException(
-							"$comparingMode is not a valid mode"
-						);
-					}
-				}
-			}
-			$lineNo++;
-			if (($lineNo + $ignoredLines) >= $linesToRead) {
-				break;
-			}
-		}
+		// Currently we don't interact with the log file on reva or OCIS
+		// So skip processing this test step.
+		return;
 	}
 
 	/**
@@ -279,90 +202,9 @@ class LoggingContext implements Context {
 		TableNode $expectedLogEntries,
 		bool $regexCompare = false
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			// So skip processing this test step.
-			return;
-		}
-		$logLines = LoggingHelper::getLogFileContent(
-			$this->featureContext->getBaseUrl(),
-			$this->featureContext->getAdminUsername(),
-			$this->featureContext->getAdminPassword(),
-			$this->featureContext->getStepLineRef()
-		);
-		$expectedLogEntries = $expectedLogEntries->getHash();
-		foreach ($logLines as $logLine) {
-			$logEntry = \json_decode($logLine, true);
-			if ($logEntry === null) {
-				throw new Exception("the log line :\n{$logLine} is not valid JSON");
-			}
-			//reindex the array, we might have deleted entries
-			$expectedLogEntries = \array_values($expectedLogEntries);
-			for ($entryNo = 0; $entryNo < \count($expectedLogEntries); $entryNo++) {
-				$count = 0;
-				$expectedLogEntry = $expectedLogEntries[$entryNo];
-				$foundLine = true;
-				foreach (\array_keys($expectedLogEntry) as $attribute) {
-					if ($expectedLogEntry[$attribute] === "") {
-						//don't check empty table entries
-						continue;
-					}
-					if (!\array_key_exists($attribute, $logEntry)) {
-						//this line does not have the attribute we are looking for
-						$foundLine = false;
-						break;
-					}
-					if (!\is_string($logEntry[$attribute])) {
-						$logEntry[$attribute] = \json_encode(
-							$logEntry[$attribute],
-							JSON_UNESCAPED_SLASHES
-						);
-					}
-
-					if ($regexCompare === true) {
-						$expectedLogEntry[$attribute]
-							= $this->featureContext->substituteInLineCodes(
-								$expectedLogEntry[$attribute],
-								null,
-								['preg_quote' => ['/']]
-							);
-						$matchAttribute = \preg_match(
-							$expectedLogEntry[$attribute],
-							$logEntry[$attribute]
-						);
-					} else {
-						$expectedLogEntry[$attribute]
-							= $this->featureContext->substituteInLineCodes(
-								$expectedLogEntry[$attribute]
-							);
-						$matchAttribute
-							= ($expectedLogEntry[$attribute] === $logEntry[$attribute]);
-					}
-					if (!$matchAttribute) {
-						$foundLine = false;
-						break;
-					}
-					if ($matchAttribute and !$shouldOrNot) {
-						$count += 1;
-						Assert::assertNotEquals(
-							$count,
-							\count($expectedLogEntry),
-							"The entry matches"
-						);
-					}
-				}
-				if ($foundLine === true) {
-					unset($expectedLogEntries[$entryNo]);
-				}
-			}
-		}
-		$notFoundLines = \print_r($expectedLogEntries, true);
-		if ($shouldOrNot) {
-			Assert::assertEmpty(
-				$expectedLogEntries,
-				"could not find these expected line(s):\n $notFoundLines"
-			);
-		}
+		// Currently we don't interact with the log file on reva or OCIS
+		// So skip processing this test step.
+		return;
 	}
 
 	/**
@@ -387,50 +229,9 @@ class LoggingContext implements Context {
 		$withOrContaining,
 		TableNode $logEntriesExpectedNotToExist
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// Currently we don't interact with the log file on reva or OCIS
-			// So skip processing this test step.
-			return;
-		}
-		$logLines = LoggingHelper::getLogFileContent(
-			$this->featureContext->getBaseUrl(),
-			$this->featureContext->getAdminUsername(),
-			$this->featureContext->getAdminPassword(),
-			$this->featureContext->getStepLineRef()
-		);
-		foreach ($logLines as $logLine) {
-			$logEntry = \json_decode($logLine, true);
-			if ($logEntry === null) {
-				throw new Exception("the log line :\n$logLine is not valid JSON");
-			}
-			foreach ($logEntriesExpectedNotToExist as $logEntryExpectedNotToExist) {
-				$match = true; // start by assuming the worst, we match the unwanted log entry
-				foreach (\array_keys($logEntryExpectedNotToExist) as $attribute) {
-					$logEntryExpectedNotToExist[$attribute]
-						= $this->featureContext->substituteInLineCodes(
-							$logEntryExpectedNotToExist[$attribute]
-						);
-
-					if (isset($logEntry[$attribute]) && ($logEntryExpectedNotToExist[$attribute] !== "")) {
-						if ($withOrContaining === 'with') {
-							$match = ($logEntryExpectedNotToExist[$attribute] === $logEntry[$attribute]);
-						} else {
-							$match = (\strpos($logEntry[$attribute], $logEntryExpectedNotToExist[$attribute]) !== false);
-						}
-					}
-					if (!isset($logEntry[$attribute])) {
-						$match = false;
-					}
-					if (!$match) {
-						break;
-					}
-				}
-			}
-			Assert::assertFalse(
-				$match,
-				"found a log entry that should not be there\n$logLine\n"
-			);
-		}
+		// Currently we don't interact with the log file on reva or OCIS
+		// So skip processing this test step.
+		return;
 	}
 
 	/**
diff --git a/tests/acceptance/features/bootstrap/Provisioning.php b/tests/acceptance/features/bootstrap/Provisioning.php
index 64ae80f9894..4164068a880 100644
--- a/tests/acceptance/features/bootstrap/Provisioning.php
+++ b/tests/acceptance/features/bootstrap/Provisioning.php
@@ -265,9 +265,7 @@ trait Provisioning {
 				);
 			}
 		} else {
-			throw new Exception(
-				__METHOD__ . ": User '$user' does not exist in the created list."
-			);
+			return false;
 		}
 	}
 
@@ -296,9 +294,7 @@ trait Provisioning {
 				);
 			}
 		} else {
-			throw new Exception(
-				__METHOD__ . ": Group '$group' does not exist in the created list."
-			);
+			return false;
 		}
 	}
 
@@ -639,26 +635,6 @@ trait Provisioning {
 		if (!$this->skipImportLdif) {
 			$this->importLdifFile($ldifFile);
 		}
-		$this->theLdapUsersHaveBeenResynced();
-	}
-
-	/**
-	 * @Given the LDAP users have been resynced
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function theLdapUsersHaveBeenReSynced():void {
-		// we need to sync ldap users when testing for parallel deployment
-		if (!OcisHelper::isTestingOnOcisOrReva() || OcisHelper::isTestingParallelDeployment()) {
-			$occResult = SetupHelper::runOcc(
-				['user:sync', 'OCA\User_LDAP\User_Proxy', '-m', 'remove'],
-				$this->getStepLineRef()
-			);
-			if ($occResult['code'] !== "0") {
-				throw new Exception(__METHOD__ . " could not sync LDAP users " . $occResult['stdErr']);
-			}
-		}
 	}
 
 	/**
@@ -766,7 +742,6 @@ trait Provisioning {
 			$this->ldap->add($newDN, $entry);
 		}
 		$this->ldapCreatedUsers[] = $setting["userid"];
-		$this->theLdapUsersHaveBeenReSynced();
 	}
 
 	/**
@@ -861,36 +836,18 @@ trait Provisioning {
 	 * @throws Exception
 	 */
 	public function deleteLdapUsersAndGroups():void {
-		$isOcisOrReva = OcisHelper::isTestingOnOcisOrReva();
 		foreach ($this->ldapCreatedUsers as $user) {
-			if ($isOcisOrReva) {
-				$this->ldap->delete(
-					"uid=" . ldap_escape($user, "", LDAP_ESCAPE_DN) . ",ou=" . $this->ldapUsersOU . "," . $this->ldapBaseDN,
-				);
-			}
+			$this->ldap->delete(
+				"uid=" . ldap_escape($user, "", LDAP_ESCAPE_DN) . ",ou=" . $this->ldapUsersOU . "," . $this->ldapBaseDN,
+			);
 			$this->rememberThatUserIsNotExpectedToExist($user);
 		}
 		foreach ($this->ldapCreatedGroups as $group) {
-			if ($isOcisOrReva) {
-				$this->ldap->delete(
-					"cn=" . ldap_escape($group, "", LDAP_ESCAPE_DN) . ",ou=" . $this->ldapGroupsOU . "," . $this->ldapBaseDN,
-				);
-			}
-			$this->rememberThatGroupIsNotExpectedToExist($group);
-		}
-		if (!$isOcisOrReva || !$this->skipImportLdif) {
-			//delete ou from LDIF import
-			$this->ldap->delete(
-				"ou=" . $this->ldapUsersOU . "," . $this->ldapBaseDN,
-				true
-			);
-			//delete all created ldap groups
 			$this->ldap->delete(
-				"ou=" . $this->ldapGroupsOU . "," . $this->ldapBaseDN,
-				true
+				"cn=" . ldap_escape($group, "", LDAP_ESCAPE_DN) . ",ou=" . $this->ldapGroupsOU . "," . $this->ldapBaseDN,
 			);
+			$this->rememberThatGroupIsNotExpectedToExist($group);
 		}
-		$this->theLdapUsersHaveBeenResynced();
 	}
 
 	/**
@@ -1034,18 +991,16 @@ trait Provisioning {
 				$attributesToCreateUser['userid'] = $userAttributes['userid'];
 				$attributesToCreateUser['password'] = $userAttributes['password'];
 				$attributesToCreateUser['displayname'] = $userAttributes['displayName'];
-				if (OcisHelper::isTestingOnOcisOrReva()) {
-					$attributesToCreateUser['username'] = $userAttributes['userid'];
-					if ($userAttributes['email'] === null) {
-						Assert::assertArrayHasKey(
-							'userid',
-							$userAttributes,
-							__METHOD__ . " userAttributes array does not have key 'userid'"
-						);
-						$attributesToCreateUser['email'] = $userAttributes['userid'] . '@owncloud.com';
-					} else {
-						$attributesToCreateUser['email'] = $userAttributes['email'];
-					}
+				$attributesToCreateUser['username'] = $userAttributes['userid'];
+				if ($userAttributes['email'] === null) {
+					Assert::assertArrayHasKey(
+						'userid',
+						$userAttributes,
+						__METHOD__ . " userAttributes array does not have key 'userid'"
+					);
+					$attributesToCreateUser['email'] = $userAttributes['userid'] . '@owncloud.com';
+				} else {
+					$attributesToCreateUser['email'] = $userAttributes['email'];
 				}
 				if ($useGraph) {
 					$body = \TestHelpers\GraphHelper::prepareCreateUserPayload(
@@ -1134,32 +1089,11 @@ trait Provisioning {
 				$userAttributes['id']
 			);
 
-			if (OcisHelper::isTestingOnOcisOrReva()) {
-				OcisHelper::createEOSStorageHome(
-					$this->getBaseUrl(),
-					$userAttributes['userid'],
-					$userAttributes['password'],
-					$this->getStepLineRef()
-				);
-				// We don't need to set displayName and email while running in oCIS
-				// As they are set when creating the user
-				continue;
-			}
-			if (isset($userAttributes['displayName'])) {
-				$editData[] = ['user' => $userAttributes['userid'], 'key' => 'displayname', 'value' => $userAttributes['displayName']];
-			}
-			if (isset($userAttributes['email'])) {
-				$editData[] = ['user' => $userAttributes['userid'], 'key' => 'email', 'value' => $userAttributes['email']];
-			}
-		}
-		// Edit the users in parallel to make the process faster.
-		if (!OcisHelper::isTestingOnOcisOrReva() && !$useLdap && \count($editData) > 0) {
-			UserHelper::editUserBatch(
+			OcisHelper::createEOSStorageHome(
 				$this->getBaseUrl(),
-				$editData,
-				$this->getAdminUsername(),
-				$this->getAdminPassword(),
-				$this->stepLineRef
+				$userAttributes['userid'],
+				$userAttributes['password'],
+				$this->getStepLineRef()
 			);
 		}
 
@@ -1171,7 +1105,7 @@ trait Provisioning {
 		// then do some work to "manually" put the skeleton files in place.
 		// When testing on ownCloud 10 the user is already getting whatever
 		// skeleton dir is defined in the server-under-test.
-		if ($skeleton && OcisHelper::isTestingOnOcisOrReva()) {
+		if ($skeleton) {
 			$this->manuallyAddSkeletonFiles($usersAttributes);
 		}
 
@@ -1461,10 +1395,8 @@ trait Provisioning {
 		$displayname = \array_key_exists("displayname", $table) ? $table["displayname"] : null;
 		$email = \array_key_exists("email", $table) ? $table["email"] : null;
 
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			if ($email === null) {
-				$email = $username . '@owncloud.com';
-			}
+		if ($email === null) {
+			$email = $username . '@owncloud.com';
 		}
 
 		$userAttributes = [
@@ -1480,9 +1412,7 @@ trait Provisioning {
 			$userAttributes[] = ["email", $email];
 		}
 
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$userAttributes[] = ["username", $username];
-		}
+		$userAttributes[] = ["username", $username];
 
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
 			$this->getAdminUsername(),
@@ -1498,9 +1428,7 @@ trait Provisioning {
 			null,
 			$this->theHTTPStatusCodeWasSuccess()
 		);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$this->manuallyAddSkeletonFilesForUser($username, $password);
-		}
+		$this->manuallyAddSkeletonFilesForUser($username, $password);
 	}
 
 	/**
@@ -1515,20 +1443,15 @@ trait Provisioning {
 	public function adminSendsUserCreationRequestUsingTheProvisioningApi(string $user, string $password):void {
 		$user = $this->getActualUsername($user);
 		$password = $this->getActualPassword($password);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$email = $user . '@owncloud.com';
-			$bodyTable = new TableNode(
-				[
-					['userid', $user],
-					['password', $password],
-					['username', $user],
-					['email', $email]
-				]
-			);
-		} else {
-			$email = null;
-			$bodyTable = new TableNode([['userid', $user], ['password', $password]]);
-		}
+		$email = $user . '@owncloud.com';
+		$bodyTable = new TableNode(
+			[
+				['userid', $user],
+				['password', $password],
+				['username', $user],
+				['email', $email]
+			]
+		);
 		$this->emptyLastHTTPStatusCodesArray();
 		$this->emptyLastOCSStatusCodesArray();
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
@@ -1547,7 +1470,7 @@ trait Provisioning {
 			null,
 			$success
 		);
-		if (OcisHelper::isTestingOnOcisOrReva() && $success) {
+		if ($success) {
 			OcisHelper::createEOSStorageHome(
 				$this->getBaseUrl(),
 				$user,
@@ -1587,20 +1510,15 @@ trait Provisioning {
 	public function userSendsUserCreationRequestUsingTheProvisioningApi(string $user, string $userToCreate, string $password):void {
 		$userToCreate = $this->getActualUsername($userToCreate);
 		$password = $this->getActualPassword($password);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$email = $userToCreate . '@owncloud.com';
-			$bodyTable = new TableNode(
-				[
-					['userid', $userToCreate],
-					['password', $password],
-					['username', $userToCreate],
-					['email', $email]
-				]
-			);
-		} else {
-			$email = null;
-			$bodyTable = new TableNode([['userid', $userToCreate], ['password', $password]]);
-		}
+		$email = $userToCreate . '@owncloud.com';
+		$bodyTable = new TableNode(
+			[
+				['userid', $userToCreate],
+				['password', $password],
+				['username', $userToCreate],
+				['email', $email]
+			]
+		);
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
 			$user,
 			"POST",
@@ -1634,23 +1552,16 @@ trait Provisioning {
 	):void {
 		$user = $this->getActualUsername($user);
 		$password = $this->getActualPassword($password);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$email = $user . '@owncloud.com';
-			$bodyTable = new TableNode(
-				[
-					['userid', $user],
-					['password', $password],
-					['username', $user],
-					['email', $email],
-					['groups[]', $group],
-				]
-			);
-		} else {
-			$email = null;
-			$bodyTable = new TableNode(
-				[['userid', $user], ['password', $password], ['groups[]', $group]]
-			);
-		}
+		$email = $user . '@owncloud.com';
+		$bodyTable = new TableNode(
+			[
+				['userid', $user],
+				['password', $password],
+				['username', $user],
+				['email', $email],
+				['groups[]', $group],
+			]
+		);
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
 			$this->getAdminUsername(),
 			"POST",
@@ -1665,9 +1576,7 @@ trait Provisioning {
 			null,
 			$this->theHTTPStatusCodeWasSuccess()
 		);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$this->manuallyAddSkeletonFilesForUser($user, $password);
-		}
+		$this->manuallyAddSkeletonFilesForUser($user, $password);
 	}
 
 	/**
@@ -1689,23 +1598,16 @@ trait Provisioning {
 	):void {
 		$userToCreate = $this->getActualUsername($userToCreate);
 		$password = $this->getActualPassword($password);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$email = $userToCreate . '@owncloud.com';
-			$bodyTable = new TableNode(
-				[
-					['userid', $userToCreate],
-					['password', $userToCreate],
-					['username', $userToCreate],
-					['email', $email],
-					['groups[]', $group],
-				]
-			);
-		} else {
-			$email = null;
-			$bodyTable = new TableNode(
-				[['userid', $userToCreate], ['password', $password], ['groups[]', $group]]
-			);
-		}
+		$email = $userToCreate . '@owncloud.com';
+		$bodyTable = new TableNode(
+			[
+				['userid', $userToCreate],
+				['password', $userToCreate],
+				['username', $userToCreate],
+				['email', $email],
+				['groups[]', $group],
+			]
+		);
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
 			$groupadmin,
 			"POST",
@@ -1720,9 +1622,7 @@ trait Provisioning {
 			null,
 			$this->theHTTPStatusCodeWasSuccess()
 		);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$this->manuallyAddSkeletonFilesForUser($userToCreate, $password);
-		}
+		$this->manuallyAddSkeletonFilesForUser($userToCreate, $password);
 	}
 
 	/**
@@ -1743,23 +1643,16 @@ trait Provisioning {
 	):void {
 		$userToCreate = $this->getActualUsername($userToCreate);
 		$password = $this->getActualPassword($password);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$email = $userToCreate . '@owncloud.com';
-			$bodyTable = new TableNode(
-				[
-					['userid', $userToCreate],
-					['password', $userToCreate],
-					['username', $userToCreate],
-					['email', $email],
-					['groups[]', $group],
-				]
-			);
-		} else {
-			$email = null;
-			$bodyTable = new TableNode(
-				[['userid', $userToCreate], ['password', $password], ['groups[]', $group]]
-			);
-		}
+		$email = $userToCreate . '@owncloud.com';
+		$bodyTable = new TableNode(
+			[
+				['userid', $userToCreate],
+				['password', $userToCreate],
+				['username', $userToCreate],
+				['email', $email],
+				['groups[]', $group],
+			]
+		);
 		$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
 			$groupadmin,
 			"POST",
@@ -3389,27 +3282,22 @@ trait Provisioning {
 		// sending the username in lowercase in the auth but in uppercase in
 		// the URL see https://github.com/owncloud/core/issues/36822
 		$user = $this->getActualUsername($user);
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// In OCIS an intermittent issue restricts users to list their own account
-			// So use admin account to list the user
-			// https://github.com/owncloud/ocis/issues/820
-			// The special code can be reverted once the issue is fixed
-			if (OcisHelper::isTestingParallelDeployment()) {
-				$requestingUser = $this->getActualUsername($user);
-				$requestingPassword = $this->getPasswordForUser($user);
-			} elseif (OcisHelper::isTestingWithGraphApi()) {
-				$requestingUser = $this->getAdminUsername();
-				$requestingPassword = $this->getAdminPassword();
-			} elseif (OcisHelper::isTestingOnOcis()) {
-				$requestingUser = 'moss';
-				$requestingPassword = 'vista';
-			} else {
-				$requestingUser = $this->getActualUsername($user);
-				$requestingPassword = $this->getPasswordForUser($requestingUser);
-			}
-		} else {
+		// In OCIS an intermittent issue restricts users to list their own account
+		// So use admin account to list the user
+		// https://github.com/owncloud/ocis/issues/820
+		// The special code can be reverted once the issue is fixed
+		if (OcisHelper::isTestingParallelDeployment()) {
+			$requestingUser = $this->getActualUsername($user);
+			$requestingPassword = $this->getPasswordForUser($user);
+		} elseif (OcisHelper::isTestingWithGraphApi()) {
 			$requestingUser = $this->getAdminUsername();
 			$requestingPassword = $this->getAdminPassword();
+		} elseif (OcisHelper::isTestingOnOcis()) {
+			$requestingUser = 'moss';
+			$requestingPassword = 'vista';
+		} else {
+			$requestingUser = $this->getActualUsername($user);
+			$requestingPassword = $this->getPasswordForUser($requestingUser);
 		}
 		$path = (OcisHelper::isTestingWithGraphApi())
 			? "/graph/v1.0"
@@ -4078,7 +3966,6 @@ trait Provisioning {
 		$ldapEntry = $this->ldap->getEntry($entry . "," . $this->ldapBaseDN);
 		Laminas\Ldap\Attribute::setAttribute($ldapEntry, $attribute, $value, $append);
 		$this->ldap->update($entry . "," . $this->ldapBaseDN, $ldapEntry);
-		$this->theLdapUsersHaveBeenReSynced();
 	}
 
 	/**
@@ -4152,7 +4039,6 @@ trait Provisioning {
 			$memberAttr,
 			"cn=$group,ou=$ou"
 		);
-		$this->theLdapUsersHaveBeenReSynced();
 	}
 
 	/**
@@ -4163,7 +4049,6 @@ trait Provisioning {
 	 */
 	public function deleteTheLdapEntry(string $entry):void {
 		$this->ldap->delete($entry . "," . $this->ldapBaseDN);
-		$this->theLdapUsersHaveBeenReSynced();
 	}
 
 	/**
@@ -4179,7 +4064,6 @@ trait Provisioning {
 			$ou = $this->getLdapGroupsOU();
 		}
 		$this->deleteTheLdapEntry("cn=$group,ou=$ou");
-		$this->theLdapUsersHaveBeenReSynced();
 		$key = \array_search($group, $this->ldapCreatedGroups);
 		if ($key !== false) {
 			unset($this->ldapCreatedGroups[$key]);
@@ -4226,7 +4110,6 @@ trait Provisioning {
 			$entry,
 			$displayName
 		);
-		$this->theLdapUsersHaveBeenReSynced();
 	}
 
 	/**
@@ -4511,7 +4394,7 @@ trait Provisioning {
 	 * @throws GuzzleException
 	 */
 	public function groupExists(string $group):bool {
-		if ($this->isTestingWithLdap() && OcisHelper::isTestingOnOcisOrReva()) {
+		if ($this->isTestingWithLdap()) {
 			$baseDN = $this->getLdapBaseDN();
 			$newDN = 'cn=' . $group . ',ou=' . $this->ldapGroupsOU . ',' . $baseDN;
 			if ($this->ldap->getEntry($newDN) !== null) {
@@ -5777,15 +5660,13 @@ trait Provisioning {
 	 */
 	public function afterScenario():void {
 		$this->waitForDavRequestsToFinish();
-		$this->restoreParametersAfterScenario();
 
-		if (OcisHelper::isTestingOnOcisOrReva() && $this->someUsersHaveBeenCreated()) {
+		if ($this->someUsersHaveBeenCreated()) {
 			foreach ($this->getCreatedUsers() as $user) {
 				OcisHelper::deleteRevaUserData($user["actualUsername"]);
 			}
-		} elseif (OcisHelper::isTestingOnOc10()) {
-			$this->resetAdminUserAttributes();
 		}
+
 		if ($this->isTestingWithLdap()) {
 			$this->deleteLdapUsersAndGroups();
 		}
@@ -5863,27 +5744,6 @@ trait Provisioning {
 		$this->usingServer($previousServer);
 	}
 
-	/**
-	 * @BeforeScenario
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function rememberAppEnabledDisabledState():void {
-		if (!OcisHelper::isTestingOnOcisOrReva()) {
-			SetupHelper::init(
-				$this->getAdminUsername(),
-				$this->getAdminPassword(),
-				$this->getBaseUrl(),
-				$this->getOcPath()
-			);
-			$this->runOcc(['app:list', '--output json']);
-			$apps = \json_decode($this->getStdOutOfOccCommand(), true);
-			$this->enabledApps = \array_keys($apps["enabled"]);
-			$this->disabledApps = \array_keys($apps["disabled"]);
-		}
-	}
-
 	/**
 	 * @BeforeScenario @rememberGroupsThatExist
 	 *
@@ -5894,34 +5754,6 @@ trait Provisioning {
 		$this->startingGroups = $this->getArrayOfGroupsResponded($this->getAllGroups());
 	}
 
-	/**
-	 * @AfterScenario
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function restoreAppEnabledDisabledState():void {
-		if (!OcisHelper::isTestingOnOcisOrReva() && !$this->isRunningForDbConversion()) {
-			$this->runOcc(['app:list', '--output json']);
-
-			$apps = \json_decode($this->getStdOutOfOccCommand(), true);
-			$currentlyEnabledApps = \array_keys($apps["enabled"]);
-			$currentlyDisabledApps = \array_keys($apps["disabled"]);
-
-			foreach ($currentlyDisabledApps as $disabledApp) {
-				if (\in_array($disabledApp, $this->enabledApps)) {
-					$this->adminEnablesOrDisablesApp('enables', $disabledApp);
-				}
-			}
-
-			foreach ($currentlyEnabledApps as $enabledApp) {
-				if (\in_array($enabledApp, $this->disabledApps)) {
-					$this->adminEnablesOrDisablesApp('disables', $enabledApp);
-				}
-			}
-		}
-	}
-
 	/**
 	 * disable or enable user
 	 *
@@ -6074,36 +5906,16 @@ trait Provisioning {
 	 * @throws Exception
 	 */
 	private function setSkeletonDirByType(string $skeletonType): string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$originalSkeletonPath = \getenv("SKELETON_DIR");
-			if ($originalSkeletonPath === false) {
-				$originalSkeletonPath = '';
-			}
-			if ($skeletonType !== '') {
-				$skeletonDirName = $skeletonType . "Skeleton";
-				$newSkeletonPath = \dirname($originalSkeletonPath) . '/' . $skeletonDirName;
-				\putenv(
-					"SKELETON_DIR=" . $newSkeletonPath
-				);
-			}
-		} else {
-			$baseUrl = $this->getBaseUrl();
-			$originalSkeletonPath = $this->getSkeletonDirectory($baseUrl);
-
-			if ($skeletonType !== '') {
-				OcsApiHelper::sendRequest(
-					$baseUrl,
-					$this->getAdminUsername(),
-					$this->getAdminPassword(),
-					'POST',
-					"/apps/testing/api/v1/testingskeletondirectory",
-					$this->getStepLineRef(),
-					[
-						'directory' => $skeletonType . "Skeleton"
-					],
-					$this->getOcsApiVersion()
-				);
-			}
+		$originalSkeletonPath = \getenv("SKELETON_DIR");
+		if ($originalSkeletonPath === false) {
+			$originalSkeletonPath = '';
+		}
+		if ($skeletonType !== '') {
+			$skeletonDirName = $skeletonType . "Skeleton";
+			$newSkeletonPath = \dirname($originalSkeletonPath) . '/' . $skeletonDirName;
+			\putenv(
+				"SKELETON_DIR=" . $newSkeletonPath
+			);
 		}
 		return $originalSkeletonPath;
 	}
@@ -6119,22 +5931,9 @@ trait Provisioning {
 	 * @throws Exception
 	 */
 	private function setSkeletonDir(string $skeletonDir): string {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			$originalSkeletonPath = \getenv("SKELETON_DIR");
-			if ($skeletonDir !== '') {
-				\putenv("SKELETON_DIR=" . $skeletonDir);
-			}
-		} else {
-			$baseUrl = $this->getBaseUrl();
-			$originalSkeletonPath = $this->getSkeletonDirectory($baseUrl);
-			if ($skeletonDir !== '') {
-				$this->runOcc(
-					["config:system:set skeletondirectory --value $skeletonDir"],
-					null,
-					null,
-					$baseUrl
-				);
-			}
+		$originalSkeletonPath = \getenv("SKELETON_DIR");
+		if ($skeletonDir !== '') {
+			\putenv("SKELETON_DIR=" . $skeletonDir);
 		}
 		return $originalSkeletonPath;
 	}
diff --git a/tests/acceptance/features/bootstrap/PublicWebDavContext.php b/tests/acceptance/features/bootstrap/PublicWebDavContext.php
index 21554982d4b..4ca9a55a52f 100644
--- a/tests/acceptance/features/bootstrap/PublicWebDavContext.php
+++ b/tests/acceptance/features/bootstrap/PublicWebDavContext.php
@@ -125,7 +125,7 @@ class PublicWebDavContext implements Context {
 	 * @return void
 	 */
 	public function deleteFileFromPublicShare(string $fileName, string $publicWebDAVAPIVersion, string $password = ""):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 		$token = $this->featureContext->getLastPublicShareToken();
@@ -179,7 +179,7 @@ class PublicWebDavContext implements Context {
 	 * @return void
 	 */
 	public function renameFileFromPublicShare(string $fileName, string $toFileName, string $publicWebDAVAPIVersion, ?string $password = ""):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 		$token = $this->featureContext->getLastPublicShareToken();
@@ -720,7 +720,7 @@ class PublicWebDavContext implements Context {
 		string $password,
 		string $expectedContent
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -869,7 +869,7 @@ class PublicWebDavContext implements Context {
 		string $password,
 		string $content
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -901,7 +901,7 @@ class PublicWebDavContext implements Context {
 		string $password,
 		string $content
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -977,7 +977,7 @@ class PublicWebDavContext implements Context {
 		string $publicWebDAVAPIVersion,
 		string $password
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -1009,7 +1009,7 @@ class PublicWebDavContext implements Context {
 		string $password,
 		string $expectedHttpCode = "401"
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -1093,7 +1093,7 @@ class PublicWebDavContext implements Context {
 		string $expectedHttpCode
 	):void {
 		$filename = "";
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 		$filename = (string)$this->featureContext->getLastPublicShareData()->data[0]->file_target;
@@ -1124,7 +1124,7 @@ class PublicWebDavContext implements Context {
 		string $publicWebDAVAPIVersion,
 		string $expectedHttpCode = null
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -1219,7 +1219,7 @@ class PublicWebDavContext implements Context {
 		$path = "whateverfilefortesting-$publicWebDAVAPIVersion-publicWebDAVAPI.txt";
 		$content = "test $publicWebDAVAPIVersion";
 
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 
@@ -1259,7 +1259,7 @@ class PublicWebDavContext implements Context {
 	):void {
 		$content = "test $publicWebDAVAPIVersion";
 		$should = ($shouldOrNot !== "not");
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		} elseif ($publicWebDAVAPIVersion === "new") {
 			$path = $this->featureContext->getLastPublicSharePath();
@@ -1435,30 +1435,18 @@ class PublicWebDavContext implements Context {
 	):void {
 		$token = $this->featureContext->getLastPublicShareToken();
 		$baseUrl = $this->featureContext->getBaseUrl();
-		if (\TestHelpers\OcisHelper::isTestingOnOcisOrReva()) {
-			$mtime = \explode(" ", $mtime);
-			\array_pop($mtime);
-			$mtime = \implode(" ", $mtime);
-			Assert::assertStringContainsString(
-				$mtime,
-				WebDavHelper::getMtimeOfFileinPublicLinkShare(
-					$baseUrl,
-					$fileName,
-					$token,
-					$this->featureContext->getStepLineRef()
-				)
-			);
-		} else {
-			Assert::assertEquals(
-				$mtime,
-				WebDavHelper::getMtimeOfFileinPublicLinkShare(
-					$baseUrl,
-					$fileName,
-					$token,
-					$this->featureContext->getStepLineRef()
-				)
-			);
-		}
+		$mtime = \explode(" ", $mtime);
+		\array_pop($mtime);
+		$mtime = \implode(" ", $mtime);
+		Assert::assertStringContainsString(
+			$mtime,
+			WebDavHelper::getMtimeOfFileinPublicLinkShare(
+				$baseUrl,
+				$fileName,
+				$token,
+				$this->featureContext->getStepLineRef()
+			)
+		);
 	}
 
 	/**
@@ -1507,7 +1495,7 @@ class PublicWebDavContext implements Context {
 		array $additionalHeaders = [],
 		string $publicWebDAVAPIVersion = "old"
 	):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 		$password = $this->featureContext->getActualPassword($password);
@@ -1599,7 +1587,7 @@ class PublicWebDavContext implements Context {
 	 * @throws GuzzleException
 	 */
 	public function publicSendsRequestToLastPublicShare(string $method, string $publicWebDAVAPIVersion, ?string $password = ''):void {
-		if (OcisHelper::isTestingOnOcisOrReva() && $publicWebDAVAPIVersion === "old") {
+		if ($publicWebDAVAPIVersion === "old") {
 			return;
 		}
 		if ($method === "PROPFIND") {
diff --git a/tests/acceptance/features/bootstrap/Sharing.php b/tests/acceptance/features/bootstrap/Sharing.php
index 38347fca68f..b5db9a36b97 100644
--- a/tests/acceptance/features/bootstrap/Sharing.php
+++ b/tests/acceptance/features/bootstrap/Sharing.php
@@ -423,17 +423,9 @@ trait Sharing {
 	 * @return void
 	 */
 	public function autoAcceptSharesHasBeenDisabled():void {
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			// auto-accept shares is disabled by default on OCIS.
-			// so there is nothing to do, just return
-			return;
-		}
-
-		$this->appConfigurationContext->serverParameterHasBeenSetTo(
-			"shareapi_auto_accept_share",
-			"core",
-			"no"
-		);
+		// auto-accept shares is disabled by default on OCIS.
+		// so there is nothing to do, just return
+		return;
 	}
 
 	/**
diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php
index af9a0b1212c..7746a29571c 100644
--- a/tests/acceptance/features/bootstrap/WebDav.php
+++ b/tests/acceptance/features/bootstrap/WebDav.php
@@ -4466,12 +4466,6 @@ trait WebDav {
 		// slashes need to stay
 		$encodedPath = \str_replace('%2F', '/', \rawurlencode($path));
 		// in ocis even brackets are encoded
-		if (OcisHelper::isTestingOnOcisOrReva()) {
-			return $encodedPath;
-		}
-		// do not encode '(' and ')' for oc10
-		$encodedPath = \str_replace('%28', '(', $encodedPath);
-		$encodedPath = \str_replace('%29', ')', $encodedPath);
 		return $encodedPath;
 	}