Repository: owncloud/ocis
Pull request #6856 (2023-07-20 11:36:59)

Convert IDP service to service trace provider.
This converts the IDP service to use the service trace provider.


Patch:
diff --git a/go.mod b/go.mod
index 425b38eb71c..da9d71f61d0 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.6.0
 	github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d
-	github.com/cs3org/reva/v2 v2.15.1-0.20230718140539-0af2a07c7fd9
+	github.com/cs3org/reva/v2 v2.15.0
 	github.com/disintegration/imaging v1.6.2
 	github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
 	github.com/egirna/icap-client v0.1.1
diff --git a/go.sum b/go.sum
index 525a672a52f..a023b23656e 100644
--- a/go.sum
+++ b/go.sum
@@ -625,8 +625,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo
 github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
 github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc=
 github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
-github.com/cs3org/reva/v2 v2.15.1-0.20230718140539-0af2a07c7fd9 h1:ycV7H1siLmMiRmc9kaS0WonysHYUT7irvH4FDDAghqQ=
-github.com/cs3org/reva/v2 v2.15.1-0.20230718140539-0af2a07c7fd9/go.mod h1:4z5EQghS2LhSWZWocH51Dw9VAs16No1zSFvFgQtgS7w=
+github.com/cs3org/reva/v2 v2.15.0 h1:saU2Heig/HswkNdDHh2Jttmhsn0nfTkvaYbXUspcNOM=
+github.com/cs3org/reva/v2 v2.15.0/go.mod h1:4z5EQghS2LhSWZWocH51Dw9VAs16No1zSFvFgQtgS7w=
 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=
diff --git a/ocis-pkg/service/http/option.go b/ocis-pkg/service/http/option.go
index 6f533c00dcb..df2095f9abb 100644
--- a/ocis-pkg/service/http/option.go
+++ b/ocis-pkg/service/http/option.go
@@ -7,6 +7,7 @@ import (
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/urfave/cli/v2"
+	"go.opentelemetry.io/otel/trace"
 )
 
 // Option defines a single option function.
@@ -14,15 +15,16 @@ type Option func(o *Options)
 
 // Options defines the available options for this package.
 type Options struct {
-	Logger    log.Logger
-	TLSConfig shared.HTTPServiceTLS
-	Namespace string
-	Name      string
-	Version   string
-	Address   string
-	Handler   http.Handler
-	Context   context.Context
-	Flags     []cli.Flag
+	Logger        log.Logger
+	TLSConfig     shared.HTTPServiceTLS
+	Namespace     string
+	Name          string
+	Version       string
+	Address       string
+	Handler       http.Handler
+	Context       context.Context
+	Flags         []cli.Flag
+	TraceProvider trace.TracerProvider
 }
 
 // newOptions initializes the available default options.
@@ -93,3 +95,10 @@ func TLSConfig(config shared.HTTPServiceTLS) Option {
 		o.TLSConfig = config
 	}
 }
+
+// TraceProvider provides a function to set the TraceProvider option.
+func TraceProvider(tp trace.TracerProvider) Option {
+	return func(o *Options) {
+		o.TraceProvider = tp
+	}
+}
diff --git a/ocis-pkg/service/http/service.go b/ocis-pkg/service/http/service.go
index cc7516ed176..8f4f6699421 100644
--- a/ocis-pkg/service/http/service.go
+++ b/ocis-pkg/service/http/service.go
@@ -10,6 +10,7 @@ import (
 	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
 
 	mhttps "github.com/go-micro/plugins/v4/server/http"
+	mtracer "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry"
 	ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
 	"go-micro.dev/v4"
 	"go-micro.dev/v4/server"
@@ -66,6 +67,15 @@ func NewService(opts ...Option) (Service, error) {
 		micro.Registry(registry.GetRegistry()),
 		micro.RegisterTTL(time.Second * 30),
 		micro.RegisterInterval(time.Second * 10),
+		micro.WrapClient(mtracer.NewClientWrapper(
+			mtracer.WithTraceProvider(sopts.TraceProvider),
+		)),
+		micro.WrapHandler(mtracer.NewHandlerWrapper(
+			mtracer.WithTraceProvider(sopts.TraceProvider),
+		)),
+		micro.WrapSubscriber(mtracer.NewSubscriberWrapper(
+			mtracer.WithTraceProvider(sopts.TraceProvider),
+		)),
 	}
 	if sopts.TLSConfig.Enabled {
 		wopts = append(wopts, micro.Metadata(map[string]string{"use_tls": "true"}))
diff --git a/services/graph/mocks/ldapclient.go b/services/graph/mocks/ldapclient.go
index a90b562032c..291ca02f6f6 100644
--- a/services/graph/mocks/ldapclient.go
+++ b/services/graph/mocks/ldapclient.go
@@ -45,8 +45,17 @@ func (_m *Client) Bind(username string, password string) error {
 }
 
 // Close provides a mock function with given fields:
-func (_m *Client) Close() {
-	_m.Called()
+func (_m *Client) Close() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
 }
 
 // Compare provides a mock function with given fields: dn, attribute, value
@@ -87,6 +96,32 @@ func (_m *Client) Del(_a0 *ldap.DelRequest) error {
 	return r0
 }
 
+// DirSync provides a mock function with given fields: searchRequest, flags, maxAttrCount, cookie
+func (_m *Client) DirSync(searchRequest *ldap.SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*ldap.SearchResult, error) {
+	ret := _m.Called(searchRequest, flags, maxAttrCount, cookie)
+
+	var r0 *ldap.SearchResult
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*ldap.SearchRequest, int64, int64, []byte) (*ldap.SearchResult, error)); ok {
+		return rf(searchRequest, flags, maxAttrCount, cookie)
+	}
+	if rf, ok := ret.Get(0).(func(*ldap.SearchRequest, int64, int64, []byte) *ldap.SearchResult); ok {
+		r0 = rf(searchRequest, flags, maxAttrCount, cookie)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*ldap.SearchResult)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*ldap.SearchRequest, int64, int64, []byte) error); ok {
+		r1 = rf(searchRequest, flags, maxAttrCount, cookie)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // ExternalBind provides a mock function with given fields:
 func (_m *Client) ExternalBind() error {
 	ret := _m.Called()
@@ -101,6 +136,20 @@ func (_m *Client) ExternalBind() error {
 	return r0
 }
 
+// GetLastError provides a mock function with given fields:
+func (_m *Client) GetLastError() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // IsClosing provides a mock function with given fields:
 func (_m *Client) IsClosing() bool {
 	ret := _m.Called()
diff --git a/services/idp/pkg/command/server.go b/services/idp/pkg/command/server.go
index b8d299f3f0b..b21f2616851 100644
--- a/services/idp/pkg/command/server.go
+++ b/services/idp/pkg/command/server.go
@@ -16,6 +16,7 @@ import (
 
 	"github.com/oklog/run"
 	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
+	"github.com/owncloud/ocis/v2/ocis-pkg/tracing"
 	"github.com/owncloud/ocis/v2/ocis-pkg/version"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/config"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/config/parser"
@@ -23,7 +24,6 @@ import (
 	"github.com/owncloud/ocis/v2/services/idp/pkg/metrics"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/server/debug"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/server/http"
-	"github.com/owncloud/ocis/v2/services/idp/pkg/tracing"
 	"github.com/urfave/cli/v2"
 )
 
@@ -50,7 +50,7 @@ func Server(cfg *config.Config) *cli.Command {
 		},
 		Action: func(c *cli.Context) error {
 			logger := logging.Configure(cfg.Service.Name, cfg.Log)
-			err := tracing.Configure(cfg)
+			traceProvider, err := tracing.GetServiceTraceProvider(cfg.Tracing, cfg.Service.Name)
 			if err != nil {
 				return err
 			}
@@ -75,8 +75,8 @@ func Server(cfg *config.Config) *cli.Command {
 					http.Context(ctx),
 					http.Config(cfg),
 					http.Metrics(metrics),
+					http.TraceProvider(traceProvider),
 				)
-
 				if err != nil {
 					logger.Info().
 						Err(err).
@@ -104,7 +104,6 @@ func Server(cfg *config.Config) *cli.Command {
 					debug.Context(ctx),
 					debug.Config(cfg),
 				)
-
 				if err != nil {
 					logger.Info().Err(err).Str("transport", "debug").Msg("Failed to initialize server")
 					return err
@@ -132,12 +131,12 @@ func ensureEncryptionSecretExists(path string) error {
 	}
 
 	dir := filepath.Dir(path)
-	err = os.MkdirAll(dir, 0700)
+	err = os.MkdirAll(dir, 0o700)
 	if err != nil {
 		return err
 	}
 
-	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
+	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o600)
 	if err != nil {
 		return err
 	}
@@ -168,12 +167,12 @@ func ensureSigningPrivateKeyExists(paths []string) error {
 		}
 
 		dir := filepath.Dir(path)
-		err = os.MkdirAll(dir, 0700)
+		err = os.MkdirAll(dir, 0o700)
 		if err != nil {
 			return err
 		}
 
-		f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
+		f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o600)
 		if err != nil {
 			return err
 		}
diff --git a/services/idp/pkg/config/tracing.go b/services/idp/pkg/config/tracing.go
index d806380bcab..8b1c8f70fba 100644
--- a/services/idp/pkg/config/tracing.go
+++ b/services/idp/pkg/config/tracing.go
@@ -1,5 +1,7 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/tracing"
+
 // Tracing defines the available tracing configuration.
 type Tracing struct {
 	Enabled   bool   `yaml:"enabled" env:"OCIS_TRACING_ENABLED;IDP_TRACING_ENABLED" desc:"Activates tracing."`
@@ -7,3 +9,13 @@ type Tracing struct {
 	Endpoint  string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;IDP_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent."`
 	Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;IDP_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset."`
 }
+
+// Convert Tracing to the tracing package's Config struct.
+func (t Tracing) Convert() tracing.Config {
+	return tracing.Config{
+		Enabled:   t.Enabled,
+		Type:      t.Type,
+		Endpoint:  t.Endpoint,
+		Collector: t.Collector,
+	}
+}
diff --git a/services/idp/pkg/middleware/static.go b/services/idp/pkg/middleware/static.go
index 440219617c7..fb1b987a07a 100644
--- a/services/idp/pkg/middleware/static.go
+++ b/services/idp/pkg/middleware/static.go
@@ -1,17 +1,17 @@
+// Package middleware provides middleware for the idp service.
 package middleware
 
 import (
 	"net/http"
 	"strings"
 
-	idpTracing "github.com/owncloud/ocis/v2/services/idp/pkg/tracing"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/codes"
 	"go.opentelemetry.io/otel/trace"
 )
 
 // Static is a middleware that serves static assets.
-func Static(root string, fs http.FileSystem) func(http.Handler) http.Handler {
+func Static(root string, fs http.FileSystem, tp trace.TracerProvider) func(http.Handler) http.Handler {
 	if !strings.HasSuffix(root, "/") {
 		root = root + "/"
 	}
@@ -28,7 +28,7 @@ func Static(root string, fs http.FileSystem) func(http.Handler) http.Handler {
 			spanOpts := []trace.SpanStartOption{
 				trace.WithSpanKind(trace.SpanKindServer),
 			}
-			ctx, span := idpTracing.TraceProvider.Tracer("idp").Start(r.Context(), "serve static asset", spanOpts...)
+			ctx, span := tp.Tracer("idp").Start(r.Context(), "serve static asset", spanOpts...)
 			defer span.End()
 			r = r.WithContext(ctx)
 
diff --git a/services/idp/pkg/server/http/option.go b/services/idp/pkg/server/http/option.go
index 84074fb87e0..2e0242e22b7 100644
--- a/services/idp/pkg/server/http/option.go
+++ b/services/idp/pkg/server/http/option.go
@@ -7,6 +7,7 @@ import (
 	"github.com/owncloud/ocis/v2/services/idp/pkg/config"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/metrics"
 	"github.com/urfave/cli/v2"
+	"go.opentelemetry.io/otel/trace"
 )
 
 // Option defines a single option function.
@@ -14,12 +15,13 @@ type Option func(o *Options)
 
 // Options defines the available options for this package.
 type Options struct {
-	Namespace string
-	Logger    log.Logger
-	Context   context.Context
-	Config    *config.Config
-	Metrics   *metrics.Metrics
-	Flags     []cli.Flag
+	Namespace     string
+	Logger        log.Logger
+	Context       context.Context
+	Config        *config.Config
+	Metrics       *metrics.Metrics
+	Flags         []cli.Flag
+	TraceProvider trace.TracerProvider
 }
 
 // newOptions initializes the available default options.
@@ -74,3 +76,10 @@ func Namespace(val string) Option {
 		o.Namespace = val
 	}
 }
+
+// TraceProvider provides a function to set the trace provider option.
+func TraceProvider(val trace.TracerProvider) Option {
+	return func(o *Options) {
+		o.TraceProvider = val
+	}
+}
diff --git a/services/idp/pkg/server/http/server.go b/services/idp/pkg/server/http/server.go
index 68c424f4e5f..74b248181ab 100644
--- a/services/idp/pkg/server/http/server.go
+++ b/services/idp/pkg/server/http/server.go
@@ -44,6 +44,7 @@ func Server(opts ...Option) (http.Service, error) {
 			Cert:    options.Config.HTTP.TLSCert,
 			Key:     options.Config.HTTP.TLSKey,
 		}),
+		http.TraceProvider(options.TraceProvider),
 	)
 	if err != nil {
 		options.Logger.Error().
@@ -69,6 +70,7 @@ func Server(opts ...Option) (http.Service, error) {
 				options.Logger,
 			),
 		),
+		svc.TraceProvider(options.TraceProvider),
 	)
 
 	{
diff --git a/services/idp/pkg/service/v0/option.go b/services/idp/pkg/service/v0/option.go
index e8ba550102c..f336c73218b 100644
--- a/services/idp/pkg/service/v0/option.go
+++ b/services/idp/pkg/service/v0/option.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/config"
+	"go.opentelemetry.io/otel/trace"
 )
 
 // Option defines a single option function.
@@ -12,9 +13,10 @@ type Option func(o *Options)
 
 // Options defines the available options for this package.
 type Options struct {
-	Logger     log.Logger
-	Config     *config.Config
-	Middleware []func(http.Handler) http.Handler
+	Logger        log.Logger
+	Config        *config.Config
+	Middleware    []func(http.Handler) http.Handler
+	TraceProvider trace.TracerProvider
 }
 
 // newOptions initializes the available default options.
@@ -48,3 +50,10 @@ func Middleware(val ...func(http.Handler) http.Handler) Option {
 		o.Middleware = val
 	}
 }
+
+// TraceProvider provides a function to set the trace provider option.
+func TraceProvider(val trace.TracerProvider) Option {
+	return func(o *Options) {
+		o.TraceProvider = val
+	}
+}
diff --git a/services/idp/pkg/service/v0/service.go b/services/idp/pkg/service/v0/service.go
index 871aa92f949..7aabdad7f85 100644
--- a/services/idp/pkg/service/v0/service.go
+++ b/services/idp/pkg/service/v0/service.go
@@ -20,10 +20,13 @@ import (
 	"github.com/libregraph/lico/server"
 	"github.com/owncloud/ocis/v2/ocis-pkg/ldap"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/ocis-pkg/tracing"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/assets"
 	cs3BackendSupport "github.com/owncloud/ocis/v2/services/idp/pkg/backends/cs3/bootstrap"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/config"
 	"github.com/owncloud/ocis/v2/services/idp/pkg/middleware"
+	"github.com/riandyrn/otelchi"
+	"go.opentelemetry.io/otel/trace"
 	"gopkg.in/yaml.v2"
 	"stash.kopano.io/kgol/rndm"
 )
@@ -81,7 +84,6 @@ func NewService(opts ...Option) Service {
 	bs, err := bootstrap.Boot(ctx, &idpSettings, &licoconfig.Config{
 		Logger: log.LogrusWrap(logger),
 	})
-
 	if err != nil {
 		logger.Fatal().Err(err).Msg("could not bootstrap idp")
 	}
@@ -94,6 +96,7 @@ func NewService(opts ...Option) Service {
 		logger: options.Logger,
 		config: options.Config,
 		assets: assetVFS,
+		tp:     options.TraceProvider,
 	}
 
 	svc.initMux(ctx, routes, handlers, options)
@@ -106,10 +109,9 @@ type temporaryClientConfig struct {
 }
 
 func createTemporaryClientsConfig(filePath, ocisURL string, clients []config.Client) error {
-
 	folder := path.Dir(filePath)
 	if _, err := os.Stat(folder); os.IsNotExist(err) {
-		if err := os.MkdirAll(folder, 0700); err != nil {
+		if err := os.MkdirAll(folder, 0o700); err != nil {
 			return err
 		}
 	}
@@ -141,18 +143,17 @@ func createTemporaryClientsConfig(filePath, ocisURL string, clients []config.Cli
 
 	defer confOnDisk.Close()
 
-	err = os.WriteFile(filePath, conf, 0600)
+	err = os.WriteFile(filePath, conf, 0o600)
 	if err != nil {
 		return err
 	}
 
 	return nil
-
 }
 
 // Init cs3 backend vars which are currently not accessible via idp api
 func initCS3EnvVars(cs3Addr, machineAuthAPIKey string) error {
-	var defaults = map[string]string{
+	defaults := map[string]string{
 		"CS3_GATEWAY":              cs3Addr,
 		"CS3_MACHINE_AUTH_API_KEY": machineAuthAPIKey,
 	}
@@ -187,7 +188,7 @@ func initLicoInternalLDAPEnvVars(ldap *config.Ldap) error {
 		filter = fmt.Sprintf("(&%s)", filter)
 	}
 
-	var defaults = map[string]string{
+	defaults := map[string]string{
 		"LDAP_URI":                 ldap.URI,
 		"LDAP_BINDDN":              ldap.BindDN,
 		"LDAP_BINDPW":              ldap.BindPassword,
@@ -221,6 +222,7 @@ type IDP struct {
 	config *config.Config
 	mux    *chi.Mux
 	assets http.FileSystem
+	tp     trace.TracerProvider
 }
 
 // initMux initializes the internal idp gorilla mux and mounts it in to a ocis chi-router
@@ -244,8 +246,18 @@ func (idp *IDP) initMux(ctx context.Context, r []server.WithRoutes, h http.Handl
 			assets.Logger(options.Logger),
 			assets.Config(options.Config),
 		),
+		idp.tp,
 	))
 
+	idp.mux.Use(
+		otelchi.Middleware(
+			"idp",
+			otelchi.WithChiRoutes(idp.mux),
+			otelchi.WithTracerProvider(idp.tp),
+			otelchi.WithPropagators(tracing.GetPropagator()),
+		),
+	)
+
 	// handle / | index.html with a template that needs to have the BASE_PREFIX replaced
 	idp.mux.Get("/signin/v1/identifier", idp.Index())
 	idp.mux.Get("/signin/v1/identifier/", idp.Index())
@@ -266,7 +278,6 @@ func (idp IDP) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 // Index renders the static html with the
 func (idp IDP) Index() http.HandlerFunc {
-
 	f, err := idp.assets.Open("/identifier/index.html")
 	if err != nil {
 		idp.logger.Fatal().Err(err).Msg("Could not open index template")
diff --git a/services/idp/pkg/service/v0/tracing.go b/services/idp/pkg/service/v0/tracing.go
deleted file mode 100644
index 4a2c04f51fa..00000000000
--- a/services/idp/pkg/service/v0/tracing.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package svc
-
-import (
-	"net/http"
-
-	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
-)
-
-// NewTracing returns a service that instruments traces.
-func NewTracing(next Service) Service {
-	return tracing{
-		next: next,
-	}
-}
-
-type tracing struct {
-	next Service
-}
-
-// ServeHTTP implements the Service interface.
-func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	middleware.TraceContext(t.next).ServeHTTP(w, r)
-}
diff --git a/services/idp/pkg/tracing/tracing.go b/services/idp/pkg/tracing/tracing.go
deleted file mode 100644
index 339597b54a6..00000000000
--- a/services/idp/pkg/tracing/tracing.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package tracing
-
-import (
-	pkgtrace "github.com/owncloud/ocis/v2/ocis-pkg/tracing"
-	"github.com/owncloud/ocis/v2/services/idp/pkg/config"
-	"go.opentelemetry.io/otel/trace"
-)
-
-var (
-	// TraceProvider is the global trace provider for the idp service.
-	TraceProvider = trace.NewNoopTracerProvider()
-)
-
-func Configure(cfg *config.Config) error {
-	var err error
-	if cfg.Tracing.Enabled {
-		if TraceProvider, err = pkgtrace.GetTraceProvider(cfg.Tracing.Endpoint, cfg.Tracing.Collector, cfg.Service.Name, cfg.Tracing.Type); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/services/storage-users/README.md b/services/storage-users/README.md
index 1ceb861c580..1622fa11f25 100644
--- a/services/storage-users/README.md
+++ b/services/storage-users/README.md
@@ -6,18 +6,6 @@ Purpose and description to be added
 
 Starting with ocis version 3.0.0, the default backend for metadata switched to messagepack. If the setting `STORAGE_USERS_OCIS_METADATA_BACKEND` has not been defined manually, the backend will be migrated to `messagepack` automatically. Though still possible to manually configure `xattrs`, this setting should not be used anymore as it will be removed in a later version.
 
-## Graceful Shutdown
-
-Starting with Infinite Scale version 3.1, you can define a graceful shutdown period for the `storage-users` service.
-
-IMPORTANT: The graceful shutdown period is only applicable if the `storage-users` service runs as standalone service. It does not apply if the `storage-users` service runs as part of the single binary or as single Docker environment. To build an environment where the `storage-users` service runs as a standalone service, you must start two instances, one _without_ the `storage-users` service and one _only with_ the the `storage-users` service. Note that both instances must be able to communicate on the same network. 
-
-When hard-stopping Infinite Scale, for example with the `kill <pid>` command (SIGKILL), it is possible and likely that not all data from the decomposedfs (metadata) has been written to the storage which may result in an inconsistent decomposedfs. When gracefully shutting down Infinite Scale, using a command like SIGTERM, the process will no longer accept any write requests from _other_ services and will try to write the internal open  requests which can take an undefined duration based on many factors. To mitigate that situation, the following things have been implemented:
-
-*   With the value of the environment variable `STORAGE_USERS_GRACEFUL_SHUTDOWN_TIMEOUT`, the `storage-users` service will delay its shutdown giving it time to finalize writing necessary data. This delay can be necessary if there is a lot of data to be saved and/or if storage access/thruput is slow. In such a case you would receive an error log entry informing you that not all data could be saved in time. To prevent such occurrences, you must increase the default value.
-
-*   If a shutdown error has been logged, the command-line maintenance tool [Inspect and Manipulate Node Metadata](https://doc.owncloud.com/ocis/next/maintenance/commands/commands.html#inspect-and-manipulate-node-metadata) can help to fix the issue. Please contact support for details. 
-
 ## CLI Commands
 
 ### Manage Unfinished Uploads
diff --git a/services/storage-users/pkg/config/config.go b/services/storage-users/pkg/config/config.go
index cec4dcdf34b..6c5d12792db 100644
--- a/services/storage-users/pkg/config/config.go
+++ b/services/storage-users/pkg/config/config.go
@@ -21,8 +21,7 @@ type Config struct {
 	TokenManager *TokenManager `yaml:"token_manager"`
 	Reva         *shared.Reva  `yaml:"reva"`
 
-	SkipUserGroupsInToken   bool `yaml:"skip_user_groups_in_token" env:"STORAGE_USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token."`
-	GracefulShutdownTimeout int  `yaml:"graceful_shutdown_timeout" env:"STORAGE_USERS_GRACEFUL_SHUTDOWN_TIMEOUT" desc:"The number of seconds to wait for the 'storage-users' service to shutdown cleanly before exiting with an error that gets logged. Note: This setting is only applicable when running the 'storage-users' service as a standalone service. See the text description for more details."`
+	SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"STORAGE_USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token."`
 
 	Driver            string            `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'ocis', Supported values are: 'ocis', 's3ng' and 'owncloudsql'. The 'ocis' driver stores all data (blob and meta data) in an POSIX compliant volume. The 's3ng' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket."`
 	Drivers           Drivers           `yaml:"drivers"`
diff --git a/services/storage-users/pkg/config/defaults/defaultconfig.go b/services/storage-users/pkg/config/defaults/defaultconfig.go
index 4674a3b1e8a..f8673c23e11 100644
--- a/services/storage-users/pkg/config/defaults/defaultconfig.go
+++ b/services/storage-users/pkg/config/defaults/defaultconfig.go
@@ -41,13 +41,12 @@ func DefaultConfig() *config.Config {
 		Service: config.Service{
 			Name: "storage-users",
 		},
-		Reva:                    shared.DefaultRevaConfig(),
-		DataServerURL:           "http://localhost:9158/data",
-		DataGatewayURL:          "https://localhost:9200/data",
-		TransferExpires:         86400,
-		UploadExpiration:        24 * 60 * 60,
-		GracefulShutdownTimeout: 30,
-		Driver:                  "ocis",
+		Reva:             shared.DefaultRevaConfig(),
+		DataServerURL:    "http://localhost:9158/data",
+		DataGatewayURL:   "https://localhost:9200/data",
+		TransferExpires:  86400,
+		UploadExpiration: 24 * 60 * 60,
+		Driver:           "ocis",
 		Drivers: config.Drivers{
 			OwnCloudSQL: config.OwnCloudSQLDriver{
 				Root:                  filepath.Join(defaults.BaseDataPath(), "storage", "owncloud"),
diff --git a/services/storage-users/pkg/revaconfig/config.go b/services/storage-users/pkg/revaconfig/config.go
index 851a38569ac..1a486ebf2a2 100644
--- a/services/storage-users/pkg/revaconfig/config.go
+++ b/services/storage-users/pkg/revaconfig/config.go
@@ -1,4 +1,3 @@
-// Package revaconfig contains the config for the reva service
 package revaconfig
 
 import (
@@ -11,12 +10,11 @@ import (
 func StorageUsersConfigFromStruct(cfg *config.Config) map[string]interface{} {
 	rcfg := map[string]interface{}{
 		"core": map[string]interface{}{
-			"tracing_enabled":           cfg.Tracing.Enabled,
-			"tracing_exporter":          cfg.Tracing.Type,
-			"tracing_endpoint":          cfg.Tracing.Endpoint,
-			"tracing_collector":         cfg.Tracing.Collector,
-			"tracing_service_name":      cfg.Service.Name,
-			"graceful_shutdown_timeout": cfg.GracefulShutdownTimeout,
+			"tracing_enabled":      cfg.Tracing.Enabled,
+			"tracing_exporter":     cfg.Tracing.Type,
+			"tracing_endpoint":     cfg.Tracing.Endpoint,
+			"tracing_collector":    cfg.Tracing.Collector,
+			"tracing_service_name": cfg.Service.Name,
 		},
 		"shared": map[string]interface{}{
 			"jwt_secret":                cfg.TokenManager.JWTSecret,
diff --git a/services/web/pkg/config/options.go b/services/web/pkg/config/options.go
index 160fe94bdad..efaeb1f1495 100644
--- a/services/web/pkg/config/options.go
+++ b/services/web/pkg/config/options.go
@@ -22,7 +22,7 @@ type Options struct {
 	OpenLinksWithDefaultApp  bool             `json:"openLinksWithDefaultApp,omitempty" yaml:"openLinksWithDefaultApp" env:"WEB_OPTION_OPEN_LINKS_WITH_DEFAULT_APP" desc:"Specifies whether single file link shares should be opened with the default app or not. If not opened by the default app, the Web UI just displays the file details. Defaults to 'true'."`
 	ImprintURL               string           `json:"imprintUrl,omitempty" yaml:"imprintUrl" env:"WEB_OPTION_IMPRINT_URL" desc:"Specifies the target URL for the imprint link valid for the ocis instance in the account menu."`
 	PrivacyURL               string           `json:"privacyUrl,omitempty" yaml:"privacyUrl" env:"WEB_OPTION_PRIVACY_URL" desc:"Specifies the target URL for the privacy link valid for the ocis instance in the account menu."`
-	AccessDeniedHelpURL      string           `json:"accessDeniedHelpUrl,omitempty" yaml:"accessDeniedHelpUrl" env:"WEB_OPTION_ACCESS_DENIED_HELP_URL" desc:"Specifies the target URL valid for the ocis instance for the generic logged out / access denied page."`
+	AccessDeniedHelpURL      string           `json:"accessDeniedHelpURL,omitempty" yaml:"accessDeniedHelpURL" env:"WEB_OPTION_ACCESS_DENIED_HELP_URL" desc:"Specifies the target URL valid for the ocis instance for the generic logged out / access denied page."`
 	TokenStorageLocal        bool             `json:"tokenStorageLocal,omitempty" yaml:"tokenStorageLocal" env:"WEB_OPTION_TOKEN_STORAGE_LOCAL" desc:"Specifies whether the access token will be stored in the local storage when set to 'true' or in the session storage when set to 'false''. If stored in the local storage, login state will be persisted across multiple browser tabs, means no additional logins are required. Defaults to 'true'."`
 }
 
diff --git a/services/webfinger/mocks/ldapclient.go b/services/webfinger/mocks/ldapclient.go
index a90b562032c..291ca02f6f6 100644
--- a/services/webfinger/mocks/ldapclient.go
+++ b/services/webfinger/mocks/ldapclient.go
@@ -45,8 +45,17 @@ func (_m *Client) Bind(username string, password string) error {
 }
 
 // Close provides a mock function with given fields:
-func (_m *Client) Close() {
-	_m.Called()
+func (_m *Client) Close() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
 }
 
 // Compare provides a mock function with given fields: dn, attribute, value
@@ -87,6 +96,32 @@ func (_m *Client) Del(_a0 *ldap.DelRequest) error {
 	return r0
 }
 
+// DirSync provides a mock function with given fields: searchRequest, flags, maxAttrCount, cookie
+func (_m *Client) DirSync(searchRequest *ldap.SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*ldap.SearchResult, error) {
+	ret := _m.Called(searchRequest, flags, maxAttrCount, cookie)
+
+	var r0 *ldap.SearchResult
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*ldap.SearchRequest, int64, int64, []byte) (*ldap.SearchResult, error)); ok {
+		return rf(searchRequest, flags, maxAttrCount, cookie)
+	}
+	if rf, ok := ret.Get(0).(func(*ldap.SearchRequest, int64, int64, []byte) *ldap.SearchResult); ok {
+		r0 = rf(searchRequest, flags, maxAttrCount, cookie)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*ldap.SearchResult)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*ldap.SearchRequest, int64, int64, []byte) error); ok {
+		r1 = rf(searchRequest, flags, maxAttrCount, cookie)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // ExternalBind provides a mock function with given fields:
 func (_m *Client) ExternalBind() error {
 	ret := _m.Called()
@@ -101,6 +136,20 @@ func (_m *Client) ExternalBind() error {
 	return r0
 }
 
+// GetLastError provides a mock function with given fields:
+func (_m *Client) GetLastError() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // IsClosing provides a mock function with given fields:
 func (_m *Client) IsClosing() bool {
 	ret := _m.Called()
diff --git a/vendor/github.com/cs3org/reva/v2/cmd/revad/internal/grace/grace.go b/vendor/github.com/cs3org/reva/v2/cmd/revad/internal/grace/grace.go
index 8aaf18aaf55..339a8c5c1e6 100644
--- a/vendor/github.com/cs3org/reva/v2/cmd/revad/internal/grace/grace.go
+++ b/vendor/github.com/cs3org/reva/v2/cmd/revad/internal/grace/grace.go
@@ -36,14 +36,13 @@ import (
 // Watcher watches a process for a graceful restart
 // preserving open network sockets to avoid packet loss.
 type Watcher struct {
-	log                     zerolog.Logger
-	graceful                bool
-	ppid                    int
-	lns                     map[string]net.Listener
-	ss                      map[string]Server
-	pidFile                 string
-	childPIDs               []int
-	gracefulShutdownTimeout int
+	log       zerolog.Logger
+	graceful  bool
+	ppid      int
+	lns       map[string]net.Listener
+	ss        map[string]Server
+	pidFile   string
+	childPIDs []int
 }
 
 // Option represent an option.
@@ -63,12 +62,6 @@ func WithPIDFile(fn string) Option {
 	}
 }
 
-func WithGracefuleShutdownTimeout(seconds int) Option {
-	return func(w *Watcher) {
-		w.gracefulShutdownTimeout = seconds
-	}
-}
-
 // NewWatcher creates a Watcher.
 func NewWatcher(opts ...Option) *Watcher {
 	w := &Watcher{
@@ -286,67 +279,49 @@ func (w *Watcher) TrapSignals() {
 			}
 
 		case syscall.SIGQUIT:
-			gracefulShutdown(w)
-		case syscall.SIGINT, syscall.SIGTERM:
-			if w.gracefulShutdownTimeout == 0 {
-				hardShutdown(w)
-			}
-			gracefulShutdown(w)
-		}
-	}
-}
-
-// TODO: Ideally this would call exit() but properly return an error. The
-// exit() is problematic (i.e. racey) especiaily when orchestrating multiple
-// reva services from some external runtime (like in the "ocis server" case
-func gracefulShutdown(w *Watcher) {
-	w.log.Info().Int("Timeout", w.gracefulShutdownTimeout).Msg("preparing for a graceful shutdown with deadline")
-	go func() {
-		count := w.gracefulShutdownTimeout
-		ticker := time.NewTicker(time.Second)
-		for ; true; <-ticker.C {
-			w.log.Info().Msgf("shutting down in %d seconds", count-1)
-			count--
-			if count <= 0 {
-				w.log.Info().Msg("deadline reached before draining active conns, hard stopping ...")
-				for _, s := range w.ss {
-					err := s.Stop()
-					if err != nil {
-						w.log.Error().Err(err).Msg("error stopping server")
+			w.log.Info().Msg("preparing for a graceful shutdown with deadline of 10 seconds")
+			go func() {
+				count := 10
+				ticker := time.NewTicker(time.Second)
+				for ; true; <-ticker.C {
+					w.log.Info().Msgf("shutting down in %d seconds", count-1)
+					count--
+					if count <= 0 {
+						w.log.Info().Msg("deadline reached before draining active conns, hard stopping ...")
+						for _, s := range w.ss {
+							err := s.Stop()
+							if err != nil {
+								w.log.Error().Err(err).Msg("error stopping server")
+							}
+							w.log.Info().Msgf("fd to %s:%s abruptly closed", s.Network(), s.Address())
+						}
+						w.Exit(1)
 					}
-					w.log.Info().Msgf("fd to %s:%s abruptly closed", s.Network(), s.Address())
 				}
-				w.Exit(1)
+			}()
+			for _, s := range w.ss {
+				w.log.Info().Msgf("fd to %s:%s gracefully closed ", s.Network(), s.Address())
+				err := s.GracefulStop()
+				if err != nil {
+					w.log.Error().Err(err).Msg("error stopping server")
+					w.log.Info().Msg("exit with error code 1")
+					w.Exit(1)
+				}
 			}
-		}
-	}()
-	for _, s := range w.ss {
-		w.log.Info().Msgf("fd to %s:%s gracefully closed ", s.Network(), s.Address())
-		err := s.GracefulStop()
-		if err != nil {
-			w.log.Error().Err(err).Msg("error stopping server")
-			w.log.Info().Msg("exit with error code 1")
-
-			w.Exit(1)
-		}
-	}
-	w.log.Info().Msg("exit with error code 0")
-	w.Exit(0)
-}
-
-// TODO: Ideally this would call exit() but properly return an error. The
-// exit() is problematic (i.e. racey) especiaily when orchestrating multiple
-// reva services from some external runtime (like in the "ocis server" case
-func hardShutdown(w *Watcher) {
-	w.log.Info().Msg("preparing for hard shutdown, aborting all conns")
-	for _, s := range w.ss {
-		w.log.Info().Msgf("fd to %s:%s abruptly closed", s.Network(), s.Address())
-		err := s.Stop()
-		if err != nil {
-			w.log.Error().Err(err).Msg("error stopping server")
+			w.log.Info().Msg("exit with error code 0")
+			w.Exit(0)
+		case syscall.SIGINT, syscall.SIGTERM:
+			w.log.Info().Msg("preparing for hard shutdown, aborting all conns")
+			for _, s := range w.ss {
+				w.log.Info().Msgf("fd to %s:%s abruptly closed", s.Network(), s.Address())
+				err := s.Stop()
+				if err != nil {
+					w.log.Error().Err(err).Msg("error stopping server")
+				}
+			}
+			w.Exit(0)
 		}
 	}
-	w.Exit(0)
 }
 
 func getListenerFile(ln net.Listener) (*os.File, error) {
diff --git a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go
index cc908d3e222..948397b7fe4 100644
--- a/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go
+++ b/vendor/github.com/cs3org/reva/v2/cmd/revad/runtime/runtime.go
@@ -72,8 +72,6 @@ type coreConf struct {
 
 	// TracingService specifies the service. i.e OpenCensus, OpenTelemetry, OpenTracing...
 	TracingService string `mapstructure:"tracing_service"`
-
-	GracefulShutdownTimeout int `mapstructure:"graceful_shutdown_timeout"`
 }
 
 func run(
@@ -94,7 +92,7 @@ func run(
 	initCPUCount(coreConf, logger)
 
 	servers := initServers(mainConf, logger, tp)
-	watcher, err := initWatcher(logger, filename, coreConf.GracefulShutdownTimeout)
+	watcher, err := initWatcher(logger, filename)
 	if err != nil {
 		log.Panic(err)
 	}
@@ -112,8 +110,8 @@ func initListeners(watcher *grace.Watcher, servers map[string]grace.Server, log
 	return listeners
 }
 
-func initWatcher(log *zerolog.Logger, filename string, gracefulShutdownTimeout int) (*grace.Watcher, error) {
-	watcher, err := handlePIDFlag(log, filename, gracefulShutdownTimeout)
+func initWatcher(log *zerolog.Logger, filename string) (*grace.Watcher, error) {
+	watcher, err := handlePIDFlag(log, filename)
 	// TODO(labkode): maybe pidfile can be created later on? like once a server is going to be created?
 	if err != nil {
 		log.Error().Err(err).Msg("error creating grace watcher")
@@ -189,11 +187,11 @@ func initLogger(conf *logConf) *zerolog.Logger {
 	return log
 }
 
-func handlePIDFlag(l *zerolog.Logger, pidFile string, gracefulShutdownTimeout int) (*grace.Watcher, error) {
-	w := grace.NewWatcher(grace.WithPIDFile(pidFile),
-		grace.WithLogger(l.With().Str("pkg", "grace").Logger()),
-		grace.WithGracefuleShutdownTimeout(gracefulShutdownTimeout),
-	)
+func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) {
+	var opts []grace.Option
+	opts = append(opts, grace.WithPIDFile(pidFile))
+	opts = append(opts, grace.WithLogger(l.With().Str("pkg", "grace").Logger()))
+	w := grace.NewWatcher(opts...)
 	err := w.WritePID()
 	if err != nil {
 		return nil, err
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/xattrs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/xattrs.go
index 39e17254763..5545f292711 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/xattrs.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/xattrs.go
@@ -49,16 +49,6 @@ func (md Attributes) SetInt64(key string, val int64) {
 	md[key] = []byte(strconv.FormatInt(val, 10))
 }
 
-// UInt64 reads an uint64 value
-func (md Attributes) UInt64(key string) (uint64, error) {
-	return strconv.ParseUint(string(md[key]), 10, 64)
-}
-
-// SetInt64 sets an uint64 value
-func (md Attributes) SetUInt64(key string, val uint64) {
-	md[key] = []byte(strconv.FormatUint(val, 10))
-}
-
 // SetXattrs sets multiple extended attributes on the write-through cache/node
 func (n *Node) SetXattrsWithContext(ctx context.Context, attribs map[string][]byte, acquireLock bool) (err error) {
 	if n.xattrsCache != nil {
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go
index cc72cced202..e04927e2a2e 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go
@@ -122,9 +122,6 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
 	metadata.SetString(prefixes.NameAttr, req.Name)
 	metadata.SetString(prefixes.SpaceNameAttr, req.Name)
 
-	// This space is empty so set initial treesize to 0
-	metadata.SetUInt64(prefixes.TreesizeAttr, 0)
-
 	if req.Type != "" {
 		metadata.SetString(prefixes.SpaceTypeAttr, req.Type)
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2c0054fa3c7..3bdf891d84b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -352,7 +352,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.15.1-0.20230718140539-0af2a07c7fd9
+# github.com/cs3org/reva/v2 v2.15.0
 ## explicit; go 1.20
 github.com/cs3org/reva/v2/cmd/revad/internal/grace
 github.com/cs3org/reva/v2/cmd/revad/runtime


Test patch:
diff --git a/tests/acceptance/expected-failures-API-on-OCIS-storage.md b/tests/acceptance/expected-failures-API-on-OCIS-storage.md
index 7436820da05..bdce9ecb723 100644
--- a/tests/acceptance/expected-failures-API-on-OCIS-storage.md
+++ b/tests/acceptance/expected-failures-API-on-OCIS-storage.md
@@ -623,6 +623,12 @@ Not everything needs to be implemented for ocis. While the oc10 testsuite covers
 - [coreApiWebdavOperations/listFiles.feature:395](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L395)
 - [coreApiWebdavOperations/listFiles.feature:400](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L400)
 
+### [graph/users: enable/disable users](https://github.com/owncloud/ocis/issues/3064)
+
+- [coreApiWebdavOperations/refuseAccess.feature:35](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L35)
+- [coreApiWebdavOperations/refuseAccess.feature:36](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L36)
+- [coreApiWebdavOperations/refuseAccess.feature:41](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L41)
+
 #### [OCS status code zero](https://github.com/owncloud/ocis/issues/3621)
 
 - [coreApiShareManagementToShares/moveReceivedShare.feature:13](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature#L13)
diff --git a/tests/acceptance/features/apiFullTextSearch/search.feature b/tests/acceptance/features/apiFullTextSearch/search.feature
index fb3fdd86856..970e9d86d34 100644
--- a/tests/acceptance/features/apiFullTextSearch/search.feature
+++ b/tests/acceptance/features/apiFullTextSearch/search.feature
@@ -25,44 +25,3 @@ Feature: full text search
       | old              |
       | new              |
       | spaces           |
-
-
-  Scenario Outline: sharee searches files by content
-    Given using <dav-path-version> DAV path
-    And user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "uploadFolder"
-    And user "Alice" has uploaded file with content "hello world from nepal" to "uploadFolder/keywordAtStart.txt"
-    And user "Alice" has uploaded file with content "saying hello to the world" to "uploadFolder/keywordAtMiddle.txt"
-    And user "Alice" has uploaded file with content "nepal want to say hello" to "uploadFolder/keywordAtLast.txt"
-    And user "Alice" has uploaded file with content "Namaste nepal" to "uploadFolder/hello.txt"
-    And user "Alice" has shared folder "/uploadFolder" with user "Brian"
-    And user "Brian" has accepted share "/uploadFolder" offered by user "Alice"
-    When user "Brian" searches for "Content:hello" using the WebDAV API
-    Then the HTTP status code should be "207"
-    And the search result of user "Brian" should contain only these files:
-      | keywordAtStart.txt  |
-      | keywordAtMiddle.txt |
-      | keywordAtLast.txt   |
-    Examples:
-      | dav-path-version |
-      | old              |
-      | new              |
-
-
-  Scenario Outline: search deleted files by content
-    Given using <dav-path-version> DAV path
-    And user "Alice" has created folder "uploadFolder"
-    And user "Alice" has uploaded file with content "hello world from nepal" to "uploadFolder/keywordAtStart.txt"
-    And user "Alice" has uploaded file with content "saying hello to the world" to "keywordAtMiddle.txt"
-    And user "Alice" has uploaded file with content "nepal want to say hello" to "keywordAtLast.txt"
-    And user "Alice" has deleted file "keywordAtLast.txt"
-    When user "Alice" searches for "Content:hello" using the WebDAV API
-    Then the HTTP status code should be "207"
-    And the search result of user "Alice" should contain only these files:
-      | keywordAtStart.txt  |
-      | keywordAtMiddle.txt |
-    Examples:
-      | dav-path-version |
-      | old              |
-      | new              |
-      | spaces           |
diff --git a/tests/acceptance/features/bootstrap/Provisioning.php b/tests/acceptance/features/bootstrap/Provisioning.php
index bb539b0e865..41d121df2dc 100644
--- a/tests/acceptance/features/bootstrap/Provisioning.php
+++ b/tests/acceptance/features/bootstrap/Provisioning.php
@@ -3825,18 +3825,9 @@ trait Provisioning {
 	 */
 	public function adminHasDisabledUserUsingTheProvisioningApi(?string $user):void {
 		$user = $this->getActualUsername($user);
-		if (OcisHelper::isTestingWithGraphApi()) {
-			$response = $this->graphContext->editUserUsingTheGraphApi($this->getAdminUsername(), $user, null, null, null, null, false);
-		} else {
-			$response = $this->disableOrEnableUser($this->getAdminUsername(), $user, 'disable');
-		}
-		Assert::assertEquals(
-			200,
-			$response->getStatusCode(),
-			__METHOD__
-			. " Expected status code is 200 but received " . $response->getStatusCode()
-			. "\nResponse body: " . $response->getBody(),
-		);
+		$this->disableOrEnableUser($this->getAdminUsername(), $user, 'disable');
+		$this->theHTTPStatusCodeShouldBeSuccess();
+		$this->ocsContext->assertOCSResponseIndicatesSuccess();
 	}
 
 	/**
@@ -5411,19 +5402,20 @@ trait Provisioning {
 	 *
 	 * @return void
 	 */
-	public function disableOrEnableUser(string $user, string $otherUser, string $action): ResponseInterface {
+	public function disableOrEnableUser(string $user, string $otherUser, string $action):void {
 		$actualUser = $this->getActualUsername($user);
 		$actualPassword = $this->getPasswordForUser($actualUser);
 		$actualOtherUser = $this->getActualUsername($otherUser);
 
 		$fullUrl = $this->getBaseUrl()
 			. "/ocs/v$this->ocsApiVersion.php/cloud/users/$actualOtherUser/$action";
-		return HttpRequestHelper::put(
+		$this->response = HttpRequestHelper::put(
 			$fullUrl,
 			$this->getStepLineRef(),
 			$actualUser,
 			$actualPassword
 		);
+		$this->pushToLastStatusCodesArrays();
 	}
 
 	/**
diff --git a/tests/acceptance/features/coreApiShareCreateSpecialToShares2/createShareWithDisabledUser.feature b/tests/acceptance/features/coreApiShareCreateSpecialToShares2/createShareWithDisabledUser.feature
index f5d5479f918..bbccb7b1708 100644
--- a/tests/acceptance/features/coreApiShareCreateSpecialToShares2/createShareWithDisabledUser.feature
+++ b/tests/acceptance/features/coreApiShareCreateSpecialToShares2/createShareWithDisabledUser.feature
@@ -24,4 +24,5 @@ Feature: share resources with a disabled user
     And user "Alice" has been disabled
     When user "Alice" shares file "textfile0.txt" with user "Brian" using the sharing API
     Then the OCS status code should be "997"
+    #And the OCS status code should be "401"
     And the HTTP status code should be "401"