Repository: owncloud/ocis
Pull request #7783 (2023-11-22 11:25:50)

feat: add a config for default link permissions
## Description
Adds a new config for default link permissions that is being announced via capabilities. It defaults to `1` (= public link with viewer permissions).

Needs a part in reva: https://github.com/cs3org/reva/pull/4358.

## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
- refs https://github.com/owncloud/web/issues/9919

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [ ] Tests only (no source changes)



Patch:
diff --git a/changelog/unreleased/add-default-link-permission-config.md b/changelog/unreleased/add-default-link-permission-config.md
new file mode 100644
index 00000000000..4ee2030843f
--- /dev/null
+++ b/changelog/unreleased/add-default-link-permission-config.md
@@ -0,0 +1,6 @@
+Enhancement: Default link permission config
+
+A new config for default link permissions that is being announced via capabilities has been added. It defaults to 1 (= public link with viewer permissions).
+
+https://github.com/owncloud/ocis/pull/7783
+https://github.com/owncloud/web/issues/9919
diff --git a/deployments/examples/ocis_wopi/config/ocis/app-registry.yaml b/deployments/examples/ocis_wopi/config/ocis/app-registry.yaml
index 4cf4262a8ea..b7cf0decf9a 100644
--- a/deployments/examples/ocis_wopi/config/ocis/app-registry.yaml
+++ b/deployments/examples/ocis_wopi/config/ocis/app-registry.yaml
@@ -35,6 +35,13 @@ app_registry:
     icon: ''
     default_app: OnlyOffice
     allow_creation: true
+  - mime_type: application/vnd.openxmlformats-officedocument.wordprocessingml.form
+    extension: docxf
+    name: Form Document
+    description: Form Document
+    icon: ''
+    default_app: OnlyOffice
+    allow_creation: true
   - mime_type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
     extension: xlsx
     name: Microsoft Excel
diff --git a/go.mod b/go.mod
index 449af173b65..aa611c3455e 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/coreos/go-oidc v2.2.1+incompatible
 	github.com/coreos/go-oidc/v3 v3.7.0
 	github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781
-	github.com/cs3org/reva/v2 v2.16.1-0.20231121150727-6f872b2efd27
+	github.com/cs3org/reva/v2 v2.16.1-0.20231122120948-0b4c31a518f7
 	github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25
 	github.com/disintegration/imaging v1.6.2
 	github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
@@ -23,7 +23,7 @@ require (
 	github.com/go-chi/chi/v5 v5.0.10
 	github.com/go-chi/cors v1.2.1
 	github.com/go-chi/render v1.0.3
-	github.com/go-ldap/ldap/v3 v3.4.5
+	github.com/go-ldap/ldap/v3 v3.4.6
 	github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3
 	github.com/go-micro/plugins/v4/client/grpc v1.2.1
 	github.com/go-micro/plugins/v4/logger/zerolog v1.2.0
@@ -184,11 +184,11 @@ require (
 	github.com/gdexlab/go-render v1.0.1 // indirect
 	github.com/ghodss/yaml v1.0.0 // indirect
 	github.com/go-acme/lego/v4 v4.4.0 // indirect
-	github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
+	github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
 	github.com/go-git/gcfg v1.5.0 // indirect
 	github.com/go-git/go-billy/v5 v5.3.1 // indirect
 	github.com/go-git/go-git/v5 v5.4.2 // indirect
-	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
+	github.com/go-jose/go-jose/v3 v3.0.1 // indirect
 	github.com/go-kit/log v0.2.1 // indirect
 	github.com/go-logfmt/logfmt v0.5.1 // indirect
 	github.com/go-logr/logr v1.3.0 // indirect
diff --git a/go.sum b/go.sum
index a98f804eb6f..d2c2651ae46 100644
--- a/go.sum
+++ b/go.sum
@@ -1013,8 +1013,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
 github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
 github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY=
 github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
-github.com/cs3org/reva/v2 v2.16.1-0.20231121150727-6f872b2efd27 h1:eIwU0jW2qIwQHaAbV65ekUnJ5dwQhaIPdoX6tPKfKGA=
-github.com/cs3org/reva/v2 v2.16.1-0.20231121150727-6f872b2efd27/go.mod h1:utPCNSrWDdAwz2biLrKvzO6nDH9L7vRVGNzof13r8Kw=
+github.com/cs3org/reva/v2 v2.16.1-0.20231122120948-0b4c31a518f7 h1:PXQMMy6BW7HTJXb4+nyNRC7E+dr/zRg2lwjc4n09ouM=
+github.com/cs3org/reva/v2 v2.16.1-0.20231122120948-0b4c31a518f7/go.mod h1:zcrrYVsBv/DwhpyO2/W5hoSZ/k6az6Z2EYQok65uqZY=
 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=
@@ -1121,8 +1121,8 @@ 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.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-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
+github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
 github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
@@ -1150,8 +1150,8 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
-github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
+github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
+github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -1161,8 +1161,8 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
 github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
 github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
 github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
-github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8=
-github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs=
+github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
+github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
 github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o=
 github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3/go.mod h1:ZXFhGda43Z2TVbfGZefXyMJzsDHhCh0go3bZUcwTx7o=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -1377,6 +1377,7 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
 github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
@@ -2146,6 +2147,7 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
 golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
 golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2495,6 +2497,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
 golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
 golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
 golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
 golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2516,6 +2519,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/services/app-registry/pkg/config/defaults/defaultconfig.go b/services/app-registry/pkg/config/defaults/defaultconfig.go
index 5cbc26c7153..121ea6eeb83 100644
--- a/services/app-registry/pkg/config/defaults/defaultconfig.go
+++ b/services/app-registry/pkg/config/defaults/defaultconfig.go
@@ -71,6 +71,13 @@ func defaultMimeTypeConfig() []config.MimeTypeConfig {
 			Description:   "Microsoft Word document",
 			AllowCreation: true,
 		},
+		{
+			MimeType:      "application/vnd.openxmlformats-officedocument.wordprocessingml.form",
+			Extension:     "docxf",
+			Name:          "Form Document",
+			Description:   "Form Document",
+			AllowCreation: true,
+		},
 		{
 			MimeType:      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
 			Extension:     "xlsx",
diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go
index fcf40a5293a..0975ac68a72 100644
--- a/services/frontend/pkg/config/config.go
+++ b/services/frontend/pkg/config/config.go
@@ -39,6 +39,7 @@ type Config struct {
 	SearchMinLength                int    `yaml:"search_min_length" env:"FRONTEND_SEARCH_MIN_LENGTH" desc:"Minimum number of characters to enter before a client should start a search for Share receivers. This setting can be used to customize the user experience if e.g too many results are displayed."`
 	Edition                        string `yaml:"edition" env:"OCIS_EDITION;FRONTEND_EDITION"`
 	DisableSSE                     bool   `yaml:"disable_sse" env:"OCIS_DISABLE_SSE;FRONTEND_DISABLE_SSE" desc:"When set to true, clients are informed that the Server-Sent Events endpoint is not accessible."`
+	DefaultLinkPermissions         int    `yaml:"default_link_permissions" env:"FRONTEND_DEFAULT_LINK_PERMISSIONS" desc:"Defines the default permissions a link is being created with. Possible values are 0 (= internal link, for instance members only) and 1 (= public link with viewer permissions). Defaults to 1."`
 
 	PublicURL string `yaml:"public_url" env:"OCIS_URL;FRONTEND_PUBLIC_URL" desc:"The public facing URL of the oCIS frontend."`
 
diff --git a/services/frontend/pkg/config/defaults/defaultconfig.go b/services/frontend/pkg/config/defaults/defaultconfig.go
index 700aebbe05e..b589aa2f97a 100644
--- a/services/frontend/pkg/config/defaults/defaultconfig.go
+++ b/services/frontend/pkg/config/defaults/defaultconfig.go
@@ -87,6 +87,7 @@ func DefaultConfig() *config.Config {
 		UploadHTTPMethodOverride: "",
 		DefaultUploadProtocol:    "tus",
 		EnableResharing:          true,
+		DefaultLinkPermissions:   1,
 		SearchMinLength:          3,
 		Edition:                  "Community",
 		Checksums: config.Checksums{
diff --git a/services/frontend/pkg/revaconfig/config.go b/services/frontend/pkg/revaconfig/config.go
index 1dc287e9919..be692a6d9a7 100644
--- a/services/frontend/pkg/revaconfig/config.go
+++ b/services/frontend/pkg/revaconfig/config.go
@@ -247,6 +247,7 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
 									"upload":                     true,
 									"multiple":                   true,
 									"supports_upload_only":       true,
+									"default_permissions":        cfg.DefaultLinkPermissions,
 									"password": map[string]interface{}{
 										"enforced": false,
 										"enforced_for": map[string]interface{}{
diff --git a/services/graph/mocks/ldapclient.go b/services/graph/mocks/ldapclient.go
index 291ca02f6f6..923a14c7eb6 100644
--- a/services/graph/mocks/ldapclient.go
+++ b/services/graph/mocks/ldapclient.go
@@ -3,6 +3,8 @@
 package mocks
 
 import (
+	context "context"
+
 	ldap "github.com/go-ldap/ldap/v3"
 	mock "github.com/stretchr/testify/mock"
 
@@ -122,6 +124,22 @@ func (_m *Client) DirSync(searchRequest *ldap.SearchRequest, flags int64, maxAtt
 	return r0, r1
 }
 
+// DirSyncAsync provides a mock function with given fields: ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie
+func (_m *Client) DirSyncAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, flags int64, maxAttrCount int64, cookie []byte) ldap.Response {
+	ret := _m.Called(ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie)
+
+	var r0 ldap.Response
+	if rf, ok := ret.Get(0).(func(context.Context, *ldap.SearchRequest, int, int64, int64, []byte) ldap.Response); ok {
+		r0 = rf(ctx, searchRequest, bufferSize, flags, maxAttrCount, cookie)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(ldap.Response)
+		}
+	}
+
+	return r0
+}
+
 // ExternalBind provides a mock function with given fields:
 func (_m *Client) ExternalBind() error {
 	ret := _m.Called()
@@ -284,6 +302,22 @@ func (_m *Client) Search(_a0 *ldap.SearchRequest) (*ldap.SearchResult, error) {
 	return r0, r1
 }
 
+// SearchAsync provides a mock function with given fields: ctx, searchRequest, bufferSize
+func (_m *Client) SearchAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int) ldap.Response {
+	ret := _m.Called(ctx, searchRequest, bufferSize)
+
+	var r0 ldap.Response
+	if rf, ok := ret.Get(0).(func(context.Context, *ldap.SearchRequest, int) ldap.Response); ok {
+		r0 = rf(ctx, searchRequest, bufferSize)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(ldap.Response)
+		}
+	}
+
+	return r0
+}
+
 // SearchWithPaging provides a mock function with given fields: searchRequest, pagingSize
 func (_m *Client) SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) {
 	ret := _m.Called(searchRequest, pagingSize)
@@ -360,6 +394,22 @@ func (_m *Client) StartTLS(_a0 *tls.Config) error {
 	return r0
 }
 
+// Syncrepl provides a mock function with given fields: ctx, searchRequest, bufferSize, mode, cookie, reloadHint
+func (_m *Client) Syncrepl(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, mode ldap.ControlSyncRequestMode, cookie []byte, reloadHint bool) ldap.Response {
+	ret := _m.Called(ctx, searchRequest, bufferSize, mode, cookie, reloadHint)
+
+	var r0 ldap.Response
+	if rf, ok := ret.Get(0).(func(context.Context, *ldap.SearchRequest, int, ldap.ControlSyncRequestMode, []byte, bool) ldap.Response); ok {
+		r0 = rf(ctx, searchRequest, bufferSize, mode, cookie, reloadHint)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(ldap.Response)
+		}
+	}
+
+	return r0
+}
+
 // TLSConnectionState provides a mock function with given fields:
 func (_m *Client) TLSConnectionState() (tls.ConnectionState, bool) {
 	ret := _m.Called()
diff --git a/services/graph/pkg/identity/ldap/reconnect.go b/services/graph/pkg/identity/ldap/reconnect.go
index 3d4d23ebbd1..263353bc1f0 100644
--- a/services/graph/pkg/identity/ldap/reconnect.go
+++ b/services/graph/pkg/identity/ldap/reconnect.go
@@ -4,6 +4,7 @@ package ldap
 // https://gist.github.com/emsearcy/cba3295d1a06d4c432ab4f6173b65e4f#file-ldap_snippet-go
 
 import (
+	"context"
 	"crypto/tls"
 	"errors"
 	"fmt"
@@ -336,6 +337,12 @@ func (c ConnWithReconnect) SearchWithPaging(searchRequest *ldap.SearchRequest, p
 	return nil, ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
 }
 
+// SearchAsync implements the ldap.Client interface
+func (c ConnWithReconnect) SearchAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int) ldap.Response {
+	// unimplemented
+	return nil
+}
+
 // NTLMUnauthenticatedBind implements the ldap.Client interface
 func (c ConnWithReconnect) NTLMUnauthenticatedBind(domain, username string) error {
 	return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
@@ -355,3 +362,15 @@ func (c ConnWithReconnect) Unbind() error {
 func (c ConnWithReconnect) DirSync(searchRequest *ldap.SearchRequest, flags, maxAttrCount int64, cookie []byte) (*ldap.SearchResult, error) {
 	return nil, ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
 }
+
+// DirSyncAsync implements the ldap.Client interface
+func (c ConnWithReconnect) DirSyncAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) ldap.Response {
+	// unimplemented
+	return nil
+}
+
+// Syncrepl implements the ldap.Client interface
+func (c ConnWithReconnect) Syncrepl(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, mode ldap.ControlSyncRequestMode, cookie []byte, reloadHint bool) ldap.Response {
+	// unimplemented
+	return nil
+}
diff --git a/services/search/pkg/query/bleve/compiler.go b/services/search/pkg/query/bleve/compiler.go
index da6cbd07695..2d0eace100b 100644
--- a/services/search/pkg/query/bleve/compiler.go
+++ b/services/search/pkg/query/bleve/compiler.go
@@ -296,6 +296,7 @@ func mimeType(k, v string) (bleveQuery.Query, bool) {
 		return bleveQuery.NewDisjunctionQuery(newQueryStringQueryList(k,
 			"application/msword",
 			"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+			"application/vnd.openxmlformats-officedocument.wordprocessingml.form",
 			"application/vnd.oasis.opendocument.text",
 			"text/plain",
 			"text/markdown",
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/mime/mime.go b/vendor/github.com/cs3org/reva/v2/pkg/mime/mime.go
index 91024917109..d2678b014b3 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/mime/mime.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/mime/mime.go
@@ -215,6 +215,7 @@ var mimeTypes = map[string]string{
 	"doc":                      "application/msword",
 	"docm":                     "application/vnd.ms-word.document.macroenabled.12",
 	"docx":                     "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+	"docxf":                    "application/vnd.openxmlformats-officedocument.wordprocessingml.form",
 	"dot":                      "application/msword",
 	"dotm":                     "application/vnd.ms-word.template.macroenabled.12",
 	"dotx":                     "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/utils/ldap/reconnect.go b/vendor/github.com/cs3org/reva/v2/pkg/utils/ldap/reconnect.go
index 7bf2b7f07d7..dc0c8a8d0de 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/utils/ldap/reconnect.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/utils/ldap/reconnect.go
@@ -22,6 +22,7 @@ package ldap
 // https://gist.github.com/emsearcy/cba3295d1a06d4c432ab4f6173b65e4f#file-ldap_snippet-go
 
 import (
+	"context"
 	"crypto/tls"
 	"errors"
 	"fmt"
@@ -306,6 +307,12 @@ func (c *ConnWithReconnect) SearchWithPaging(searchRequest *ldap.SearchRequest,
 	return nil, ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
 }
 
+// SearchAsync implements the ldap.Client interface
+func (c *ConnWithReconnect) SearchAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int) ldap.Response {
+	// unimplemented
+	return nil
+}
+
 // NTLMUnauthenticatedBind implements the ldap.Client interface
 func (c *ConnWithReconnect) NTLMUnauthenticatedBind(domain, username string) error {
 	return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
@@ -325,3 +332,15 @@ func (c *ConnWithReconnect) Unbind() error {
 func (c *ConnWithReconnect) DirSync(searchRequest *ldap.SearchRequest, flags, maxAttrCount int64, cookie []byte) (*ldap.SearchResult, error) {
 	return nil, ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
 }
+
+// DirSyncAsync implements the ldap.Client interface
+func (c *ConnWithReconnect) DirSyncAsync(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) ldap.Response {
+	// unimplemented
+	return nil
+}
+
+// Syncrepl implements the ldap.Client interface
+func (c *ConnWithReconnect) Syncrepl(ctx context.Context, searchRequest *ldap.SearchRequest, bufferSize int, mode ldap.ControlSyncRequestMode, cookie []byte, reloadHint bool) ldap.Response {
+	// unimplemented
+	return nil
+}
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
index 4fd7a66e18f..bafa78631a5 100644
--- a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
@@ -170,12 +170,10 @@ func PrintPacket(p *Packet) {
 	printPacket(os.Stdout, p, 0, false)
 }
 
-func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
-	indentStr := ""
-
-	for len(indentStr) != indent {
-		indentStr += " "
-	}
+// Return a string describing packet content. This is not recursive,
+// If the packet is a sequence, use `printPacket()`, or browse
+// sequence yourself.
+func DescribePacket(p *Packet) string {
 
 	classStr := ClassMap[p.ClassType]
 
@@ -194,7 +192,17 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
 		description = p.Description + ": "
 	}
 
-	_, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
+	return fmt.Sprintf("%s(%s, %s, %s) Len=%d %q", description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
+}
+
+func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
+	indentStr := ""
+
+	for len(indentStr) != indent {
+		indentStr += " "
+	}
+
+	_, _ = fmt.Fprintf(out, "%s%s\n", indentStr, DescribePacket(p))
 
 	if printBytes {
 		PrintBytes(out, p.Bytes(), indentStr)
@@ -317,7 +325,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 			// Read the next packet
 			child, r, err := readPacket(reader)
 			if err != nil {
-				return nil, read, err
+				return nil, read, unexpectedEOF(err)
 			}
 			contentRead += r
 			read += r
@@ -348,10 +356,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 	if length > 0 {
 		_, err := io.ReadFull(reader, content)
 		if err != nil {
-			if err == io.EOF {
-				return nil, read, io.ErrUnexpectedEOF
-			}
-			return nil, read, err
+			return nil, read, unexpectedEOF(err)
 		}
 		read += length
 	}
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
index e8c435749a6..c501d8190c0 100644
--- a/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
@@ -37,7 +37,7 @@ func readIdentifier(reader io.Reader) (Identifier, int, error) {
 			if Debug {
 				fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
 			}
-			return Identifier{}, read, err
+			return Identifier{}, read, unexpectedEOF(err)
 		}
 		tagBytes++
 		read++
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/length.go b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
index 9cc195d0bdf..2c81cc3fd29 100644
--- a/vendor/github.com/go-asn1-ber/asn1-ber/length.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
@@ -13,7 +13,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
 		if Debug {
 			fmt.Printf("error reading length byte: %v\n", err)
 		}
-		return 0, 0, err
+		return 0, 0, unexpectedEOF(err)
 	}
 	read++
 
@@ -47,7 +47,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
 				if Debug {
 					fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
 				}
-				return 0, read, err
+				return 0, read, unexpectedEOF(err)
 			}
 			read++
 
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/real.go b/vendor/github.com/go-asn1-ber/asn1-ber/real.go
index 610a003a734..9f637a572a2 100644
--- a/vendor/github.com/go-asn1-ber/asn1-ber/real.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/real.go
@@ -89,12 +89,18 @@ func parseBinaryFloat(v []byte) (float64, error) {
 	case 0x02:
 		expLen = 3
 	case 0x03:
+		if len(v) < 2 {
+			return 0.0, errors.New("invalid data")
+		}
 		expLen = int(v[0])
 		if expLen > 8 {
 			return 0.0, errors.New("too big value of exponent")
 		}
 		v = v[1:]
 	}
+	if expLen > len(v) {
+		return 0.0, errors.New("too big value of exponent")
+	}
 	buf, v = v[:expLen], v[expLen:]
 	exponent, err := ParseInt64(buf)
 	if err != nil {
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/util.go b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
index 14dc87d7c92..da45e9fa55b 100644
--- a/vendor/github.com/go-asn1-ber/asn1-ber/util.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
@@ -6,14 +6,18 @@ func readByte(reader io.Reader) (byte, error) {
 	bytes := make([]byte, 1)
 	_, err := io.ReadFull(reader, bytes)
 	if err != nil {
-		if err == io.EOF {
-			return 0, io.ErrUnexpectedEOF
-		}
 		return 0, err
 	}
 	return bytes[0], nil
 }
 
+func unexpectedEOF(err error) error {
+	if err == io.EOF {
+		return io.ErrUnexpectedEOF
+	}
+	return err
+}
+
 func isEOCPacket(p *Packet) bool {
 	return p != nil &&
 		p.Tag == TagEOC &&
diff --git a/vendor/github.com/go-jose/go-jose/v3/CHANGELOG.md b/vendor/github.com/go-jose/go-jose/v3/CHANGELOG.md
new file mode 100644
index 00000000000..7820c2f4d78
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/CHANGELOG.md
@@ -0,0 +1,8 @@
+# v3.0.1
+
+Fixed:
+ - Security issue: an attacker specifying a large "p2c" value can cause
+   JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
+   amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
+   disclosure and to Tom Tervoort for originally publishing the category of attack.
+   https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
diff --git a/vendor/github.com/go-jose/go-jose/v3/symmetric.go b/vendor/github.com/go-jose/go-jose/v3/symmetric.go
index fb54775ed61..1ffd2708b21 100644
--- a/vendor/github.com/go-jose/go-jose/v3/symmetric.go
+++ b/vendor/github.com/go-jose/go-jose/v3/symmetric.go
@@ -415,6 +415,11 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
 		if p2c <= 0 {
 			return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: must be a positive integer")
 		}
+		if p2c > 1000000 {
+			// An unauthenticated attacker can set a high P2C value. Set an upper limit to avoid
+			// DoS attacks.
+			return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: too high")
+		}
 
 		// salt is UTF8(Alg) || 0x00 || Salt Input
 		alg := headers.getAlgorithm()
diff --git a/vendor/github.com/go-ldap/ldap/v3/client.go b/vendor/github.com/go-ldap/ldap/v3/client.go
index b438d254b28..ed96e840aa6 100644
--- a/vendor/github.com/go-ldap/ldap/v3/client.go
+++ b/vendor/github.com/go-ldap/ldap/v3/client.go
@@ -1,6 +1,7 @@
 package ldap
 
 import (
+	"context"
 	"crypto/tls"
 	"time"
 )
@@ -32,6 +33,9 @@ type Client interface {
 	PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
 
 	Search(*SearchRequest) (*SearchResult, error)
+	SearchAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response
 	SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
 	DirSync(searchRequest *SearchRequest, flags, maxAttrCount int64, cookie []byte) (*SearchResult, error)
+	DirSyncAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) Response
+	Syncrepl(ctx context.Context, searchRequest *SearchRequest, bufferSize int, mode ControlSyncRequestMode, cookie []byte, reloadHint bool) Response
 }
diff --git a/vendor/github.com/go-ldap/ldap/v3/conn.go b/vendor/github.com/go-ldap/ldap/v3/conn.go
index 3ed8088312b..6d08362145b 100644
--- a/vendor/github.com/go-ldap/ldap/v3/conn.go
+++ b/vendor/github.com/go-ldap/ldap/v3/conn.go
@@ -288,9 +288,9 @@ func (l *Conn) Close() (err error) {
 		l.chanMessage <- &messagePacket{Op: MessageQuit}
 
 		timeoutCtx := context.Background()
-		if l.requestTimeout > 0 {
+		if l.getTimeout() > 0 {
 			var cancelFunc context.CancelFunc
-			timeoutCtx, cancelFunc = context.WithTimeout(timeoutCtx, time.Duration(l.requestTimeout))
+			timeoutCtx, cancelFunc = context.WithTimeout(timeoutCtx, time.Duration(l.getTimeout()))
 			defer cancelFunc()
 		}
 		select {
@@ -316,6 +316,10 @@ func (l *Conn) SetTimeout(timeout time.Duration) {
 	atomic.StoreInt64(&l.requestTimeout, int64(timeout))
 }
 
+func (l *Conn) getTimeout() int64 {
+	return atomic.LoadInt64(&l.requestTimeout)
+}
+
 // Returns the next available messageID
 func (l *Conn) nextMessageID() int64 {
 	if messageID, ok := <-l.chanMessageID; ok {
@@ -325,8 +329,10 @@ func (l *Conn) nextMessageID() int64 {
 }
 
 // GetLastError returns the last recorded error from goroutines like processMessages and reader.
-// // Only the last recorded error will be returned.
+// Only the last recorded error will be returned.
 func (l *Conn) GetLastError() error {
+	l.messageMutex.Lock()
+	defer l.messageMutex.Unlock()
 	return l.err
 }
 
@@ -484,7 +490,7 @@ func (l *Conn) processMessages() {
 			// If we are closing due to an error, inform anyone who
 			// is waiting about the error.
 			if l.IsClosing() && l.closeErr.Load() != nil {
-				msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}, time.Duration(l.requestTimeout))
+				msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}, time.Duration(l.getTimeout()))
 			}
 			l.Debug.Printf("Closing channel for MessageID %d", messageID)
 			close(msgCtx.responses)
@@ -512,7 +518,7 @@ func (l *Conn) processMessages() {
 				_, err := l.conn.Write(buf)
 				if err != nil {
 					l.Debug.Printf("Error Sending Message: %s", err.Error())
-					message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}, time.Duration(l.requestTimeout))
+					message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}, time.Duration(l.getTimeout()))
 					close(message.Context.responses)
 					break
 				}
@@ -522,9 +528,10 @@ func (l *Conn) processMessages() {
 				l.messageContexts[message.MessageID] = message.Context
 
 				// Add timeout if defined
-				if l.requestTimeout > 0 {
+				requestTimeout := l.getTimeout()
+				if requestTimeout > 0 {
 					go func() {
-						timer := time.NewTimer(time.Duration(l.requestTimeout))
+						timer := time.NewTimer(time.Duration(requestTimeout))
 						defer func() {
 							if err := recover(); err != nil {
 								l.err = fmt.Errorf("ldap: recovered panic in RequestTimeout: %v", err)
@@ -547,7 +554,7 @@ func (l *Conn) processMessages() {
 			case MessageResponse:
 				l.Debug.Printf("Receiving message %d", message.MessageID)
 				if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
-					msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.requestTimeout))
+					msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.getTimeout()))
 				} else {
 					l.err = fmt.Errorf("ldap: received unexpected message %d, %v", message.MessageID, l.IsClosing())
 					l.Debug.PrintPacket(message.Packet)
@@ -557,7 +564,7 @@ func (l *Conn) processMessages() {
 				// All reads will return immediately
 				if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
 					l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
-					msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))}, time.Duration(l.requestTimeout))
+					msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))}, time.Duration(l.getTimeout()))
 					delete(l.messageContexts, message.MessageID)
 					close(msgCtx.responses)
 				}
diff --git a/vendor/github.com/go-ldap/ldap/v3/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go
index 0be697e220e..60453deb13b 100644
--- a/vendor/github.com/go-ldap/ldap/v3/control.go
+++ b/vendor/github.com/go-ldap/ldap/v3/control.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 
 	ber "github.com/go-asn1-ber/asn1-ber"
+	"github.com/google/uuid"
 )
 
 const (
@@ -36,6 +37,15 @@ const (
 	ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309"
 	// ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
 	ControlTypeDirSync = "1.2.840.113556.1.4.841"
+
+	// ControlTypeSyncRequest - https://www.ietf.org/rfc/rfc4533.txt
+	ControlTypeSyncRequest = "1.3.6.1.4.1.4203.1.9.1.1"
+	// ControlTypeSyncState - https://www.ietf.org/rfc/rfc4533.txt
+	ControlTypeSyncState = "1.3.6.1.4.1.4203.1.9.1.2"
+	// ControlTypeSyncDone - https://www.ietf.org/rfc/rfc4533.txt
+	ControlTypeSyncDone = "1.3.6.1.4.1.4203.1.9.1.3"
+	// ControlTypeSyncInfo - https://www.ietf.org/rfc/rfc4533.txt
+	ControlTypeSyncInfo = "1.3.6.1.4.1.4203.1.9.1.4"
 )
 
 // Flags for DirSync control
@@ -58,6 +68,10 @@ var ControlTypeMap = map[string]string{
 	ControlTypeServerSideSorting:       "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
 	ControlTypeServerSideSortingResult: "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
 	ControlTypeDirSync:                 "DirSync",
+	ControlTypeSyncRequest:             "Sync Request",
+	ControlTypeSyncState:               "Sync State",
+	ControlTypeSyncDone:                "Sync Done",
+	ControlTypeSyncInfo:                "Sync Info",
 }
 
 // Control defines an interface controls provide to encode and describe themselves
@@ -390,7 +404,13 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
 
 	case 2:
 		packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
-		ControlType = packet.Children[0].Value.(string)
+		if packet.Children[0].Value != nil {
+			ControlType = packet.Children[0].Value.(string)
+		} else if packet.Children[0].Data != nil {
+			ControlType = packet.Children[0].Data.String()
+		} else {
+			return nil, fmt.Errorf("not found where to get the control type")
+		}
 
 		// Children[1] could be criticality or value (both are optional)
 		// duck-type on whether this is a boolean
@@ -514,29 +534,28 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
 		return NewControlServerSideSortingResult(value)
 	case ControlTypeDirSync:
 		value.Description += " (DirSync)"
-		c := new(ControlDirSync)
-		if value.Value != nil {
-			valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
-			if err != nil {
-				return nil, err
-			}
-			value.Data.Truncate(0)
-			value.Value = nil
-			value.AppendChild(valueChildren)
+		return NewResponseControlDirSync(value)
+	case ControlTypeSyncState:
+		value.Description += " (Sync State)"
+		valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode data bytes: %s", err)
 		}
-		value = value.Children[0]
-		if len(value.Children) != 3 { // also on initial creation, Cookie is an empty string
-			return nil, fmt.Errorf("invalid number of children in dirSync control")
+		return NewControlSyncState(valueChildren)
+	case ControlTypeSyncDone:
+		value.Description += " (Sync Done)"
+		valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode data bytes: %s", err)
 		}
-		value.Description = "DirSync Control Value"
-		value.Children[0].Description = "Flags"
-		value.Children[1].Description = "MaxAttrCnt"
-		value.Children[2].Description = "Cookie"
-		c.Flags = value.Children[0].Value.(int64)
-		c.MaxAttrCnt = value.Children[1].Value.(int64)
-		c.Cookie = value.Children[2].Data.Bytes()
-		value.Children[2].Value = c.Cookie
-		return c, nil
+		return NewControlSyncDone(valueChildren)
+	case ControlTypeSyncInfo:
+		value.Description += " (Sync Info)"
+		valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+		}
+		return NewControlSyncInfo(valueChildren)
 	default:
 		c := new(ControlString)
 		c.ControlType = ControlType
@@ -610,18 +629,57 @@ func encodeControls(controls []Control) *ber.Packet {
 
 // ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
 type ControlDirSync struct {
-	Flags      int64
-	MaxAttrCnt int64
-	Cookie     []byte
+	Criticality  bool
+	Flags        int64
+	MaxAttrCount int64
+	Cookie       []byte
 }
 
-// NewControlDirSync returns a dir sync control
+// @deprecated Use NewRequestControlDirSync instead
 func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync {
+	return NewRequestControlDirSync(flags, maxAttrCount, cookie)
+}
+
+// NewRequestControlDirSync returns a dir sync control
+func NewRequestControlDirSync(
+	flags int64, maxAttrCount int64, cookie []byte,
+) *ControlDirSync {
 	return &ControlDirSync{
-		Flags:      flags,
-		MaxAttrCnt: maxAttrCount,
-		Cookie:     cookie,
+		Criticality:  true,
+		Flags:        flags,
+		MaxAttrCount: maxAttrCount,
+		Cookie:       cookie,
+	}
+}
+
+// NewResponseControlDirSync returns a dir sync control
+func NewResponseControlDirSync(value *ber.Packet) (*ControlDirSync, error) {
+	if value.Value != nil {
+		valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+		}
+		value.Data.Truncate(0)
+		value.Value = nil
+		value.AppendChild(valueChildren)
+	}
+	child := value.Children[0]
+	if len(child.Children) != 3 { // also on initial creation, Cookie is an empty string
+		return nil, fmt.Errorf("invalid number of children in dirSync control")
 	}
+	child.Description = "DirSync Control Value"
+	child.Children[0].Description = "Flags"
+	child.Children[1].Description = "MaxAttrCount"
+	child.Children[2].Description = "Cookie"
+
+	cookie := child.Children[2].Data.Bytes()
+	child.Children[2].Value = cookie
+	return &ControlDirSync{
+		Criticality:  true,
+		Flags:        child.Children[0].Value.(int64),
+		MaxAttrCount: child.Children[1].Value.(int64),
+		Cookie:       cookie,
+	}, nil
 }
 
 // GetControlType returns the OID
@@ -631,28 +689,33 @@ func (c *ControlDirSync) GetControlType() string {
 
 // String returns a human-readable description
 func (c *ControlDirSync) String() string {
-	return fmt.Sprintf("ControlType: %s (%q), Criticality: true, ControlValue: Flags: %d, MaxAttrCnt: %d", ControlTypeMap[ControlTypeDirSync], ControlTypeDirSync, c.Flags, c.MaxAttrCnt)
+	return fmt.Sprintf(
+		"ControlType: %s (%q) Criticality: %t ControlValue: Flags: %d MaxAttrCount: %d",
+		ControlTypeMap[ControlTypeDirSync],
+		ControlTypeDirSync,
+		c.Criticality,
+		c.Flags,
+		c.MaxAttrCount,
+	)
 }
 
 // Encode returns the ber packet representation
 func (c *ControlDirSync) Encode() *ber.Packet {
+	cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie")
+	if len(c.Cookie) != 0 {
+		cookie.Value = c.Cookie
+		cookie.Data.Write(c.Cookie)
+	}
+
 	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
 	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")"))
-	packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, true, "Criticality")) // must be true always
+	packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always
 
 	val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)")
-
 	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value")
 	seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags"))
-	seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCnt), "MaxAttrCount"))
-
-	cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie")
-	if len(c.Cookie) != 0 {
-		cookie.Value = c.Cookie
-		cookie.Data.Write(c.Cookie)
-	}
+	seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCount), "MaxAttrCount"))
 	seq.AppendChild(cookie)
-
 	val.AppendChild(seq)
 
 	packet.AppendChild(val)
@@ -850,3 +913,382 @@ func (c *ControlServerSideSortingResult) String() string {
 		c.Result,
 	)
 }
+
+// Mode for ControlTypeSyncRequest
+type ControlSyncRequestMode int64
+
+const (
+	SyncRequestModeRefreshOnly       ControlSyncRequestMode = 1
+	SyncRequestModeRefreshAndPersist ControlSyncRequestMode = 3
+)
+
+// ControlSyncRequest implements the Sync Request Control described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncRequest struct {
+	Criticality bool
+	Mode        ControlSyncRequestMode
+	Cookie      []byte
+	ReloadHint  bool
+}
+
+func NewControlSyncRequest(
+	mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
+) *ControlSyncRequest {
+	return &ControlSyncRequest{
+		Criticality: true,
+		Mode:        mode,
+		Cookie:      cookie,
+		ReloadHint:  reloadHint,
+	}
+}
+
+// GetControlType returns the OID
+func (c *ControlSyncRequest) GetControlType() string {
+	return ControlTypeSyncRequest
+}
+
+// Encode encodes the control
+func (c *ControlSyncRequest) Encode() *ber.Packet {
+	_mode := int64(c.Mode)
+	mode := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, _mode, "Mode")
+	var cookie *ber.Packet
+	if len(c.Cookie) > 0 {
+		cookie = ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
+		cookie.Value = c.Cookie
+		cookie.Data.Write(c.Cookie)
+	}
+	reloadHint := ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.ReloadHint, "Reload Hint")
+
+	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSyncRequest, "Control Type ("+ControlTypeMap[ControlTypeSyncRequest]+")"))
+	packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
+
+	val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Sync Request)")
+	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Sync Request Value")
+	seq.AppendChild(mode)
+	if cookie != nil {
+		seq.AppendChild(cookie)
+	}
+	seq.AppendChild(reloadHint)
+	val.AppendChild(seq)
+
+	packet.AppendChild(val)
+	return packet
+}
+
+// String returns a human-readable description
+func (c *ControlSyncRequest) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)  Criticality: %t Mode: %d Cookie: %s ReloadHint: %t",
+		ControlTypeMap[ControlTypeSyncRequest],
+		ControlTypeSyncRequest,
+		c.Criticality,
+		c.Mode,
+		string(c.Cookie),
+		c.ReloadHint,
+	)
+}
+
+// State for ControlSyncState
+type ControlSyncStateState int64
+
+const (
+	SyncStatePresent ControlSyncStateState = 0
+	SyncStateAdd     ControlSyncStateState = 1
+	SyncStateModify  ControlSyncStateState = 2
+	SyncStateDelete  ControlSyncStateState = 3
+)
+
+// ControlSyncState implements the Sync State Control described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncState struct {
+	Criticality bool
+	State       ControlSyncStateState
+	EntryUUID   uuid.UUID
+	Cookie      []byte
+}
+
+func NewControlSyncState(pkt *ber.Packet) (*ControlSyncState, error) {
+	var (
+		state     ControlSyncStateState
+		entryUUID uuid.UUID
+		cookie    []byte
+		err       error
+	)
+	switch len(pkt.Children) {
+	case 0, 1:
+		return nil, fmt.Errorf("at least two children are required: %d", len(pkt.Children))
+	case 2:
+		state = ControlSyncStateState(pkt.Children[0].Value.(int64))
+		entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode uuid: %w", err)
+		}
+	case 3:
+		state = ControlSyncStateState(pkt.Children[0].Value.(int64))
+		entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode uuid: %w", err)
+		}
+		cookie = pkt.Children[2].ByteValue
+	}
+	return &ControlSyncState{
+		Criticality: false,
+		State:       state,
+		EntryUUID:   entryUUID,
+		Cookie:      cookie,
+	}, nil
+}
+
+// GetControlType returns the OID
+func (c *ControlSyncState) GetControlType() string {
+	return ControlTypeSyncState
+}
+
+// Encode encodes the control
+func (c *ControlSyncState) Encode() *ber.Packet {
+	return nil
+}
+
+// String returns a human-readable description
+func (c *ControlSyncState) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)  Criticality: %t State: %d EntryUUID: %s Cookie: %s",
+		ControlTypeMap[ControlTypeSyncState],
+		ControlTypeSyncState,
+		c.Criticality,
+		c.State,
+		c.EntryUUID.String(),
+		string(c.Cookie),
+	)
+}
+
+// ControlSyncDone implements the Sync Done Control described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncDone struct {
+	Criticality    bool
+	Cookie         []byte
+	RefreshDeletes bool
+}
+
+func NewControlSyncDone(pkt *ber.Packet) (*ControlSyncDone, error) {
+	var (
+		cookie         []byte
+		refreshDeletes bool
+	)
+	switch len(pkt.Children) {
+	case 0:
+		// have nothing to do
+	case 1:
+		cookie = pkt.Children[0].ByteValue
+	case 2:
+		cookie = pkt.Children[0].ByteValue
+		refreshDeletes = pkt.Children[1].Value.(bool)
+	}
+	return &ControlSyncDone{
+		Criticality:    false,
+		Cookie:         cookie,
+		RefreshDeletes: refreshDeletes,
+	}, nil
+}
+
+// GetControlType returns the OID
+func (c *ControlSyncDone) GetControlType() string {
+	return ControlTypeSyncDone
+}
+
+// Encode encodes the control
+func (c *ControlSyncDone) Encode() *ber.Packet {
+	return nil
+}
+
+// String returns a human-readable description
+func (c *ControlSyncDone) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)  Criticality: %t Cookie: %s RefreshDeletes: %t",
+		ControlTypeMap[ControlTypeSyncDone],
+		ControlTypeSyncDone,
+		c.Criticality,
+		string(c.Cookie),
+		c.RefreshDeletes,
+	)
+}
+
+// Tag For ControlSyncInfo
+type ControlSyncInfoValue uint64
+
+const (
+	SyncInfoNewcookie      ControlSyncInfoValue = 0
+	SyncInfoRefreshDelete  ControlSyncInfoValue = 1
+	SyncInfoRefreshPresent ControlSyncInfoValue = 2
+	SyncInfoSyncIdSet      ControlSyncInfoValue = 3
+)
+
+// ControlSyncInfoNewCookie implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncInfoNewCookie struct {
+	Cookie []byte
+}
+
+// String returns a human-readable description
+func (c *ControlSyncInfoNewCookie) String() string {
+	return fmt.Sprintf(
+		"NewCookie[Cookie: %s]",
+		string(c.Cookie),
+	)
+}
+
+// ControlSyncInfoRefreshDelete implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncInfoRefreshDelete struct {
+	Cookie      []byte
+	RefreshDone bool
+}
+
+// String returns a human-readable description
+func (c *ControlSyncInfoRefreshDelete) String() string {
+	return fmt.Sprintf(
+		"RefreshDelete[Cookie: %s RefreshDone: %t]",
+		string(c.Cookie),
+		c.RefreshDone,
+	)
+}
+
+// ControlSyncInfoRefreshPresent implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncInfoRefreshPresent struct {
+	Cookie      []byte
+	RefreshDone bool
+}
+
+// String returns a human-readable description
+func (c *ControlSyncInfoRefreshPresent) String() string {
+	return fmt.Sprintf(
+		"RefreshPresent[Cookie: %s RefreshDone: %t]",
+		string(c.Cookie),
+		c.RefreshDone,
+	)
+}
+
+// ControlSyncInfoSyncIdSet implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncInfoSyncIdSet struct {
+	Cookie         []byte
+	RefreshDeletes bool
+	SyncUUIDs      []uuid.UUID
+}
+
+// String returns a human-readable description
+func (c *ControlSyncInfoSyncIdSet) String() string {
+	return fmt.Sprintf(
+		"SyncIdSet[Cookie: %s RefreshDeletes: %t SyncUUIDs: %v]",
+		string(c.Cookie),
+		c.RefreshDeletes,
+		c.SyncUUIDs,
+	)
+}
+
+// ControlSyncInfo implements the Sync Info Control described in https://www.ietf.org/rfc/rfc4533.txt
+type ControlSyncInfo struct {
+	Criticality    bool
+	Value          ControlSyncInfoValue
+	NewCookie      *ControlSyncInfoNewCookie
+	RefreshDelete  *ControlSyncInfoRefreshDelete
+	RefreshPresent *ControlSyncInfoRefreshPresent
+	SyncIdSet      *ControlSyncInfoSyncIdSet
+}
+
+func NewControlSyncInfo(pkt *ber.Packet) (*ControlSyncInfo, error) {
+	var (
+		cookie         []byte
+		refreshDone    = true
+		refreshDeletes bool
+		syncUUIDs      []uuid.UUID
+	)
+	c := &ControlSyncInfo{Criticality: false}
+	switch ControlSyncInfoValue(pkt.Identifier.Tag) {
+	case SyncInfoNewcookie:
+		c.Value = SyncInfoNewcookie
+		c.NewCookie = &ControlSyncInfoNewCookie{
+			Cookie: pkt.ByteValue,
+		}
+	case SyncInfoRefreshDelete:
+		c.Value = SyncInfoRefreshDelete
+		switch len(pkt.Children) {
+		case 0:
+			// have nothing to do
+		case 1:
+			cookie = pkt.Children[0].ByteValue
+		case 2:
+			cookie = pkt.Children[0].ByteValue
+			refreshDone = pkt.Children[1].Value.(bool)
+		}
+		c.RefreshDelete = &ControlSyncInfoRefreshDelete{
+			Cookie:      cookie,
+			RefreshDone: refreshDone,
+		}
+	case SyncInfoRefreshPresent:
+		c.Value = SyncInfoRefreshPresent
+		switch len(pkt.Children) {
+		case 0:
+			// have nothing to do
+		case 1:
+			cookie = pkt.Children[0].ByteValue
+		case 2:
+			cookie = pkt.Children[0].ByteValue
+			refreshDone = pkt.Children[1].Value.(bool)
+		}
+		c.RefreshPresent = &ControlSyncInfoRefreshPresent{
+			Cookie:      cookie,
+			RefreshDone: refreshDone,
+		}
+	case SyncInfoSyncIdSet:
+		c.Value = SyncInfoSyncIdSet
+		switch len(pkt.Children) {
+		case 0:
+			// have nothing to do
+		case 1:
+			cookie = pkt.Children[0].ByteValue
+		case 2:
+			cookie = pkt.Children[0].ByteValue
+			refreshDeletes = pkt.Children[1].Value.(bool)
+		case 3:
+			cookie = pkt.Children[0].ByteValue
+			refreshDeletes = pkt.Children[1].Value.(bool)
+			syncUUIDs = make([]uuid.UUID, 0, len(pkt.Children[2].Children))
+			for _, child := range pkt.Children[2].Children {
+				u, err := uuid.FromBytes(child.ByteValue)
+				if err != nil {
+					return nil, fmt.Errorf("failed to decode uuid: %w", err)
+				}
+				syncUUIDs = append(syncUUIDs, u)
+			}
+		}
+		c.SyncIdSet = &ControlSyncInfoSyncIdSet{
+			Cookie:         cookie,
+			RefreshDeletes: refreshDeletes,
+			SyncUUIDs:      syncUUIDs,
+		}
+	default:
+		return nil, fmt.Errorf("unknown sync info value: %d", pkt.Identifier.Tag)
+	}
+	return c, nil
+}
+
+// GetControlType returns the OID
+func (c *ControlSyncInfo) GetControlType() string {
+	return ControlTypeSyncInfo
+}
+
+// Encode encodes the control
+func (c *ControlSyncInfo) Encode() *ber.Packet {
+	return nil
+}
+
+// String returns a human-readable description
+func (c *ControlSyncInfo) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)  Criticality: %t Value: %d %s %s %s %s",
+		ControlTypeMap[ControlTypeSyncInfo],
+		ControlTypeSyncInfo,
+		c.Criticality,
+		c.Value,
+		c.NewCookie,
+		c.RefreshDelete,
+		c.RefreshPresent,
+		c.SyncIdSet,
+	)
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/error.go b/vendor/github.com/go-ldap/ldap/v3/error.go
index 3cdb7b318c4..3c2559ef700 100644
--- a/vendor/github.com/go-ldap/ldap/v3/error.go
+++ b/vendor/github.com/go-ldap/ldap/v3/error.go
@@ -192,6 +192,8 @@ func (e *Error) Error() string {
 	return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
 }
 
+func (e *Error) Unwrap() error { return e.Err }
+
 // GetLDAPError creates an Error out of a BER packet representing a LDAPResult
 // The return is an error object. It can be casted to a Error structure.
 // This function returns nil if resultCode in the LDAPResult sequence is success(0).
@@ -206,15 +208,21 @@ func GetLDAPError(packet *ber.Packet) error {
 			return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet"), Packet: packet}
 		}
 		if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
-			resultCode := uint16(response.Children[0].Value.(int64))
-			if resultCode == 0 { // No error
-				return nil
-			}
-			return &Error{
-				ResultCode: resultCode,
-				MatchedDN:  response.Children[1].Value.(string),
-				Err:        fmt.Errorf("%s", response.Children[2].Value.(string)),
-				Packet:     packet,
+			if ber.Type(response.Children[0].Tag) == ber.Type(ber.TagInteger) || ber.Type(response.Children[0].Tag) == ber.Type(ber.TagEnumerated) {
+				resultCode := uint16(response.Children[0].Value.(int64))
+				if resultCode == 0 { // No error
+					return nil
+				}
+
+				if ber.Type(response.Children[1].Tag) == ber.Type(ber.TagOctetString) &&
+					ber.Type(response.Children[2].Tag) == ber.Type(ber.TagOctetString) {
+					return &Error{
+						ResultCode: resultCode,
+						MatchedDN:  response.Children[1].Value.(string),
+						Err:        fmt.Errorf("%s", response.Children[2].Value.(string)),
+						Packet:     packet,
+					}
+				}
 			}
 		}
 	}
diff --git a/vendor/github.com/go-ldap/ldap/v3/ldap.go b/vendor/github.com/go-ldap/ldap/v3/ldap.go
index e2c758fbcc6..90837a7743a 100644
--- a/vendor/github.com/go-ldap/ldap/v3/ldap.go
+++ b/vendor/github.com/go-ldap/ldap/v3/ldap.go
@@ -32,6 +32,7 @@ const (
 	ApplicationSearchResultReference = 19
 	ApplicationExtendedRequest       = 23
 	ApplicationExtendedResponse      = 24
+	ApplicationIntermediateResponse  = 25
 )
 
 // ApplicationMap contains human readable descriptions of LDAP Application Codes
@@ -56,6 +57,7 @@ var ApplicationMap = map[uint8]string{
 	ApplicationSearchResultReference: "Search Result Reference",
 	ApplicationExtendedRequest:       "Extended Request",
 	ApplicationExtendedResponse:      "Extended Response",
+	ApplicationIntermediateResponse:  "Intermediate Response",
 }
 
 // Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
diff --git a/vendor/github.com/go-ldap/ldap/v3/response.go b/vendor/github.com/go-ldap/ldap/v3/response.go
new file mode 100644
index 00000000000..1abe02a37f2
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/response.go
@@ -0,0 +1,207 @@
+package ldap
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// Response defines an interface to get data from an LDAP server
+type Response interface {
+	Entry() *Entry
+	Referral() string
+	Controls() []Control
+	Err() error
+	Next() bool
+}
+
+type searchResponse struct {
+	conn *Conn
+	ch   chan *SearchSingleResult
+
+	entry    *Entry
+	referral string
+	controls []Control
+	err      error
+}
+
+// Entry returns an entry from the given search request
+func (r *searchResponse) Entry() *Entry {
+	return r.entry
+}
+
+// Referral returns a referral from the given search request
+func (r *searchResponse) Referral() string {
+	return r.referral
+}
+
+// Controls returns controls from the given search request
+func (r *searchResponse) Controls() []Control {
+	return r.controls
+}
+
+// Err returns an error when the given search request was failed
+func (r *searchResponse) Err() error {
+	return r.err
+}
+
+// Next returns whether next data exist or not
+func (r *searchResponse) Next() bool {
+	res, ok := <-r.ch
+	if !ok {
+		return false
+	}
+	if res == nil {
+		return false
+	}
+	r.err = res.Error
+	if r.err != nil {
+		return false
+	}
+	r.entry = res.Entry
+	r.referral = res.Referral
+	r.controls = res.Controls
+	return true
+}
+
+func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest) {
+	go func() {
+		defer func() {
+			close(r.ch)
+			if err := recover(); err != nil {
+				r.conn.err = fmt.Errorf("ldap: recovered panic in searchResponse: %v", err)
+			}
+		}()
+
+		if r.conn.IsClosing() {
+			return
+		}
+
+		packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+		packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, r.conn.nextMessageID(), "MessageID"))
+		// encode search request
+		err := searchRequest.appendTo(packet)
+		if err != nil {
+			r.ch <- &SearchSingleResult{Error: err}
+			return
+		}
+		r.conn.Debug.PrintPacket(packet)
+
+		msgCtx, err := r.conn.sendMessage(packet)
+		if err != nil {
+			r.ch <- &SearchSingleResult{Error: err}
+			return
+		}
+		defer r.conn.finishMessage(msgCtx)
+
+		foundSearchSingleResultDone := false
+		for !foundSearchSingleResultDone {
+			select {
+			case <-ctx.Done():
+				r.conn.Debug.Printf("%d: %s", msgCtx.id, ctx.Err().Error())
+				return
+			default:
+				r.conn.Debug.Printf("%d: waiting for response", msgCtx.id)
+				packetResponse, ok := <-msgCtx.responses
+				if !ok {
+					err := NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
+					r.ch <- &SearchSingleResult{Error: err}
+					return
+				}
+				packet, err = packetResponse.ReadPacket()
+				r.conn.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+				if err != nil {
+					r.ch <- &SearchSingleResult{Error: err}
+					return
+				}
+
+				if r.conn.Debug {
+					if err := addLDAPDescriptions(packet); err != nil {
+						r.ch <- &SearchSingleResult{Error: err}
+						return
+					}
+					ber.PrintPacket(packet)
+				}
+
+				switch packet.Children[1].Tag {
+				case ApplicationSearchResultEntry:
+					result := &SearchSingleResult{
+						Entry: &Entry{
+							DN:         packet.Children[1].Children[0].Value.(string),
+							Attributes: unpackAttributes(packet.Children[1].Children[1].Children),
+						},
+					}
+					if len(packet.Children) != 3 {
+						r.ch <- result
+						continue
+					}
+					decoded, err := DecodeControl(packet.Children[2].Children[0])
+					if err != nil {
+						werr := fmt.Errorf("failed to decode search result entry: %w", err)
+						result.Error = werr
+						r.ch <- result
+						return
+					}
+					result.Controls = append(result.Controls, decoded)
+					r.ch <- result
+
+				case ApplicationSearchResultDone:
+					if err := GetLDAPError(packet); err != nil {
+						r.ch <- &SearchSingleResult{Error: err}
+						return
+					}
+					if len(packet.Children) == 3 {
+						result := &SearchSingleResult{}
+						for _, child := range packet.Children[2].Children {
+							decodedChild, err := DecodeControl(child)
+							if err != nil {
+								werr := fmt.Errorf("failed to decode child control: %w", err)
+								r.ch <- &SearchSingleResult{Error: werr}
+								return
+							}
+							result.Controls = append(result.Controls, decodedChild)
+						}
+						r.ch <- result
+					}
+					foundSearchSingleResultDone = true
+
+				case ApplicationSearchResultReference:
+					ref := packet.Children[1].Children[0].Value.(string)
+					r.ch <- &SearchSingleResult{Referral: ref}
+
+				case ApplicationIntermediateResponse:
+					decoded, err := DecodeControl(packet.Children[1])
+					if err != nil {
+						werr := fmt.Errorf("failed to decode intermediate response: %w", err)
+						r.ch <- &SearchSingleResult{Error: werr}
+						return
+					}
+					result := &SearchSingleResult{}
+					result.Controls = append(result.Controls, decoded)
+					r.ch <- result
+
+				default:
+					err := fmt.Errorf("unknown tag: %d", packet.Children[1].Tag)
+					r.ch <- &SearchSingleResult{Error: err}
+					return
+				}
+			}
+		}
+		r.conn.Debug.Printf("%d: returning", msgCtx.id)
+	}()
+}
+
+func newSearchResponse(conn *Conn, bufferSize int) *searchResponse {
+	var ch chan *SearchSingleResult
+	if bufferSize > 0 {
+		ch = make(chan *SearchSingleResult, bufferSize)
+	} else {
+		ch = make(chan *SearchSingleResult)
+	}
+	return &searchResponse{
+		conn: conn,
+		ch:   ch,
+	}
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/search.go b/vendor/github.com/go-ldap/ldap/v3/search.go
index 9c0ccd07175..4eb10762db4 100644
--- a/vendor/github.com/go-ldap/ldap/v3/search.go
+++ b/vendor/github.com/go-ldap/ldap/v3/search.go
@@ -1,6 +1,7 @@
 package ldap
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"reflect"
@@ -377,6 +378,28 @@ func (s *SearchResult) appendTo(r *SearchResult) {
 	r.Controls = append(r.Controls, s.Controls...)
 }
 
+// SearchSingleResult holds the server's single entry response to a search request
+type SearchSingleResult struct {
+	// Entry is the returned entry
+	Entry *Entry
+	// Referral is the returned referral
+	Referral string
+	// Controls are the returned controls
+	Controls []Control
+	// Error is set when the search request was failed
+	Error error
+}
+
+// Print outputs a human-readable description
+func (s *SearchSingleResult) Print() {
+	s.Entry.Print()
+}
+
+// PrettyPrint outputs a human-readable description with indenting
+func (s *SearchSingleResult) PrettyPrint(indent int) {
+	s.Entry.PrettyPrint(indent)
+}
+
 // SearchRequest represents a search request to send to the server
 type SearchRequest struct {
 	BaseDN       string
@@ -561,6 +584,32 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
 	}
 }
 
+// SearchAsync performs a search request and returns all search results asynchronously.
+// This means you get all results until an error happens (or the search successfully finished),
+// e.g. for size / time limited requests all are recieved until the limit is reached.
+// To stop the search, call cancel function of the context.
+func (l *Conn) SearchAsync(
+	ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response {
+	r := newSearchResponse(l, bufferSize)
+	r.start(ctx, searchRequest)
+	return r
+}
+
+// Syncrepl is a short name for LDAP Sync Replication engine that works on the
+// consumer-side. This can perform a persistent search and returns an entry
+// when the entry is updated on the server side.
+// To stop the search, call cancel function of the context.
+func (l *Conn) Syncrepl(
+	ctx context.Context, searchRequest *SearchRequest, bufferSize int,
+	mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
+) Response {
+	control := NewControlSyncRequest(mode, cookie, reloadHint)
+	searchRequest.Controls = append(searchRequest.Controls, control)
+	r := newSearchResponse(l, bufferSize)
+	r.start(ctx, searchRequest)
+	return r
+}
+
 // unpackAttributes will extract all given LDAP attributes and it's values
 // from the ber.Packet
 func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
@@ -586,55 +635,56 @@ func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
 }
 
 // DirSync does a Search with dirSync Control.
-func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*SearchResult, error) {
-	var dirSyncControl *ControlDirSync
-
+func (l *Conn) DirSync(
+	searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte,
+) (*SearchResult, error) {
 	control := FindControl(searchRequest.Controls, ControlTypeDirSync)
 	if control == nil {
-		dirSyncControl = NewControlDirSync(flags, maxAttrCount, cookie)
-		searchRequest.Controls = append(searchRequest.Controls, dirSyncControl)
+		c := NewRequestControlDirSync(flags, maxAttrCount, cookie)
+		searchRequest.Controls = append(searchRequest.Controls, c)
 	} else {
-		castControl, ok := control.(*ControlDirSync)
-		if !ok {
-			return nil, fmt.Errorf("Expected DirSync control to be of type *ControlDirSync, got %v", control)
-		}
-		if castControl.Flags != flags {
-			return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", castControl.Flags, flags)
+		c := control.(*ControlDirSync)
+		if c.Flags != flags {
+			return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", c.Flags, flags)
 		}
-		if castControl.MaxAttrCnt != maxAttrCount {
-			return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", castControl.MaxAttrCnt, maxAttrCount)
+		if c.MaxAttrCount != maxAttrCount {
+			return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", c.MaxAttrCount, maxAttrCount)
 		}
-		dirSyncControl = castControl
 	}
-	searchResult := new(SearchResult)
-	result, err := l.Search(searchRequest)
+	searchResult, err := l.Search(searchRequest)
 	l.Debug.Printf("Looking for result...")
 	if err != nil {
-		return searchResult, err
+		return nil, err
 	}
-	if result == nil {
-		return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
+	if searchResult == nil {
+		return nil, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
 	}
 
-	searchResult.Entries = append(searchResult.Entries, result.Entries...)
-	searchResult.Referrals = append(searchResult.Referrals, result.Referrals...)
-	searchResult.Controls = append(searchResult.Controls, result.Controls...)
-
 	l.Debug.Printf("Looking for DirSync Control...")
-	dirSyncResult := FindControl(result.Controls, ControlTypeDirSync)
-	if dirSyncResult == nil {
-		dirSyncControl = nil
+	resultControl := FindControl(searchResult.Controls, ControlTypeDirSync)
+	if resultControl == nil {
 		l.Debug.Printf("Could not find dirSyncControl control.  Breaking...")
 		return searchResult, nil
 	}
 
-	cookie = dirSyncResult.(*ControlDirSync).Cookie
+	cookie = resultControl.(*ControlDirSync).Cookie
 	if len(cookie) == 0 {
-		dirSyncControl = nil
 		l.Debug.Printf("Could not find cookie.  Breaking...")
 		return searchResult, nil
 	}
-	dirSyncControl.SetCookie(cookie)
 
 	return searchResult, nil
 }
+
+// DirSyncDirSyncAsync performs a search request and returns all search results
+// asynchronously. This is efficient when the server returns lots of entries.
+func (l *Conn) DirSyncAsync(
+	ctx context.Context, searchRequest *SearchRequest, bufferSize int,
+	flags, maxAttrCount int64, cookie []byte,
+) Response {
+	control := NewRequestControlDirSync(flags, maxAttrCount, cookie)
+	searchRequest.Controls = append(searchRequest.Controls, control)
+	r := newSearchResponse(l, bufferSize)
+	r.start(ctx, searchRequest)
+	return r
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 248943fddab..2ab45bf0948 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -357,7 +357,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1
 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
 github.com/cs3org/go-cs3apis/cs3/types/v1beta1
-# github.com/cs3org/reva/v2 v2.16.1-0.20231121150727-6f872b2efd27
+# github.com/cs3org/reva/v2 v2.16.1-0.20231122120948-0b4c31a518f7
 ## explicit; go 1.20
 github.com/cs3org/reva/v2/cmd/revad/internal/grace
 github.com/cs3org/reva/v2/cmd/revad/runtime
@@ -800,7 +800,7 @@ github.com/ghodss/yaml
 ## explicit; go 1.15
 github.com/go-acme/lego/v4/acme
 github.com/go-acme/lego/v4/challenge
-# github.com/go-asn1-ber/asn1-ber v1.5.4
+# github.com/go-asn1-ber/asn1-ber v1.5.5
 ## explicit; go 1.13
 github.com/go-asn1-ber/asn1-ber
 # github.com/go-chi/chi/v5 v5.0.10
@@ -871,7 +871,7 @@ github.com/go-git/go-git/v5/utils/merkletrie/filesystem
 github.com/go-git/go-git/v5/utils/merkletrie/index
 github.com/go-git/go-git/v5/utils/merkletrie/internal/frame
 github.com/go-git/go-git/v5/utils/merkletrie/noder
-# github.com/go-jose/go-jose/v3 v3.0.0
+# github.com/go-jose/go-jose/v3 v3.0.1
 ## explicit; go 1.12
 github.com/go-jose/go-jose/v3
 github.com/go-jose/go-jose/v3/cipher
@@ -880,7 +880,7 @@ github.com/go-jose/go-jose/v3/json
 ## explicit; go 1.17
 github.com/go-kit/log
 github.com/go-kit/log/level
-# github.com/go-ldap/ldap/v3 v3.4.5
+# github.com/go-ldap/ldap/v3 v3.4.6
 ## explicit; go 1.14
 github.com/go-ldap/ldap/v3
 # github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3


Test patch:
diff --git a/services/search/pkg/query/bleve/compiler_test.go b/services/search/pkg/query/bleve/compiler_test.go
index f1e5fc307ac..597ce4c29e5 100644
--- a/services/search/pkg/query/bleve/compiler_test.go
+++ b/services/search/pkg/query/bleve/compiler_test.go
@@ -396,6 +396,7 @@ func Test_compile(t *testing.T) {
 			want: query.NewDisjunctionQuery([]query.Query{
 				query.NewQueryStringQuery(`MimeType:application/msword`),
 				query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document`),
+				query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.form`),
 				query.NewQueryStringQuery(`MimeType:application/vnd.oasis.opendocument.text`),
 				query.NewQueryStringQuery(`MimeType:text/plain`),
 				query.NewQueryStringQuery(`MimeType:text/markdown`),
@@ -417,6 +418,7 @@ func Test_compile(t *testing.T) {
 				query.NewDisjunctionQuery([]query.Query{
 					query.NewQueryStringQuery(`MimeType:application/msword`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document`),
+					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.form`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.oasis.opendocument.text`),
 					query.NewQueryStringQuery(`MimeType:text/plain`),
 					query.NewQueryStringQuery(`MimeType:text/markdown`),
@@ -441,6 +443,7 @@ func Test_compile(t *testing.T) {
 			want: query.NewDisjunctionQuery([]query.Query{
 				query.NewQueryStringQuery(`MimeType:application/msword`),
 				query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document`),
+				query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.form`),
 				query.NewQueryStringQuery(`MimeType:application/vnd.oasis.opendocument.text`),
 				query.NewQueryStringQuery(`MimeType:text/plain`),
 				query.NewQueryStringQuery(`MimeType:text/markdown`),
@@ -470,6 +473,7 @@ func Test_compile(t *testing.T) {
 				query.NewDisjunctionQuery([]query.Query{
 					query.NewQueryStringQuery(`MimeType:application/msword`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document`),
+					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.form`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.oasis.opendocument.text`),
 					query.NewQueryStringQuery(`MimeType:text/plain`),
 					query.NewQueryStringQuery(`MimeType:text/markdown`),
@@ -499,6 +503,7 @@ func Test_compile(t *testing.T) {
 					query.NewQueryStringQuery(`MimeType:application/pdf`),
 					query.NewQueryStringQuery(`MimeType:application/msword`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document`),
+					query.NewQueryStringQuery(`MimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.form`),
 					query.NewQueryStringQuery(`MimeType:application/vnd.oasis.opendocument.text`),
 					query.NewQueryStringQuery(`MimeType:text/plain`),
 					query.NewQueryStringQuery(`MimeType:text/markdown`),