Repository: owncloud/ocis
Pull request #5426 (2023-01-19 09:36:22)

fix populating user drive and drives
continuation of https://github.com/owncloud/ocis/pull/5421

The personal `drive` should not be part of the `drives` list.

And when decomposedfs supports the space owner filter we should use that instead of a user + space type "personal" filter, but not part of this PR.


Patch:
diff --git a/.drone.star b/.drone.star
index 38e51c05712..23214b14739 100644
--- a/.drone.star
+++ b/.drone.star
@@ -889,7 +889,7 @@ def coreApiTests(ctx, part_number = 1, number_of_parts = 1, storage = "ocis", ac
                              "UPLOAD_DELETE_WAIT_TIME": "1" if storage == "owncloud" else 0,
                          },
                          "commands": [
-                             "make -C %s test-acceptance-core-api" % (dirs["base"]),
+                             "make -C %s test-acceptance-from-core-api" % (dirs["base"]),
                          ],
                      },
                  ] + failEarly(ctx, early_fail),
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78f64c73034..d437b7d19e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ The following sections list the changes for unreleased.
 * Enhancement - Resource tags: [#5227](https://github.com/owncloud/ocis/pull/5227)
 * Enhancement - Expiration Notifications: [#5330](https://github.com/owncloud/ocis/pull/5330)
 * Enhancement - Add expiration to user and group shares: [#5389](https://github.com/owncloud/ocis/pull/5389)
+* Enhancement - Space Management permissions: [#5441](https://github.com/owncloud/ocis/pull/5441)
 
 # Changelog for [2.0.0] (2022-11-30)
 
diff --git a/Makefile b/Makefile
index d9e993d8ea1..61c4ef3820e 100644
--- a/Makefile
+++ b/Makefile
@@ -69,7 +69,7 @@ help:
 	@echo -e "${GREEN}Testing with test suite natively installed:${RESET}\n"
 	@echo -e "${PURPLE}\tdocs: https://owncloud.dev/ocis/development/testing/#testing-with-test-suite-natively-installed${RESET}\n"
 	@echo -e "\tmake test-acceptance-api\t\t${BLUE}run API acceptance tests${RESET}"
-	@echo -e "\tmake test-acceptance-core-api\t\t${BLUE}run core API acceptance tests${RESET}"
+	@echo -e "\tmake test-acceptance-from-core-api\t\t${BLUE}run core API acceptance tests${RESET}"
 	@echo -e "\tmake test-paralleldeployment-api\t${BLUE}run API acceptance tests for parallel deployment${RESET}"
 	@echo -e "\tmake clean-tests\t\t\t${BLUE}delete API tests framework dependencies${RESET}"
 	@echo
@@ -111,8 +111,8 @@ CORE_BEHAT_YML=tests/acceptance/config/behat-core.yml
 test-acceptance-api: vendor-bin/behat/vendor
 	BEHAT_BIN=$(BEHAT_BIN) $(PWD)/tests/acceptance/run.sh --type api
 
-.PHONY: test-acceptance-core-api
-test-acceptance-core-api: vendor-bin/behat/vendor
+.PHONY: test-acceptance-from-core-api
+test-acceptance-from-core-api: vendor-bin/behat/vendor
 	BEHAT_BIN=$(BEHAT_BIN) BEHAT_YML=$(CORE_BEHAT_YML) $(PWD)/tests/acceptance/run.sh --type core-api
 
 .PHONY: test-paralleldeployment-api
diff --git a/changelog/unreleased/populate-expanded-properties.md b/changelog/unreleased/populate-expanded-properties.md
index b3ea06a4357..81c242e9544 100644
--- a/changelog/unreleased/populate-expanded-properties.md
+++ b/changelog/unreleased/populate-expanded-properties.md
@@ -3,4 +3,5 @@ Bugfix: Populate expanded properties
 We now return an empty array when an expanded relation has no entries. This makes consuming the responses a little easier.
 
 https://github.com/owncloud/ocis/pull/5421
-https://github.com/owncloud/ocis/issues/5419
\ No newline at end of file
+https://github.com/owncloud/ocis/issues/5419
+https://github.com/owncloud/ocis/pull/5426
\ No newline at end of file
diff --git a/changelog/unreleased/space-management-permissions.md b/changelog/unreleased/space-management-permissions.md
new file mode 100644
index 00000000000..24dfc0fcd2d
--- /dev/null
+++ b/changelog/unreleased/space-management-permissions.md
@@ -0,0 +1,5 @@
+Enhancement: Space Management permissions
+
+We added new space management permissions. `space-properties` will allow changing space properties (name, description, ...). `space-ability` will allow enabling and disabling spaces
+
+https://github.com/owncloud/ocis/pull/5441
diff --git a/go.mod b/go.mod
index 1bdc9918e80..374eb4faa22 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/blevesearch/bleve/v2 v2.3.5
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965
-	github.com/cs3org/reva/v2 v2.12.1-0.20230113095557-1a7bb8c77ed0
+	github.com/cs3org/reva/v2 v2.12.1-0.20230125142028-2d125a5354d0
 	github.com/disintegration/imaging v1.6.2
 	github.com/ggwhite/go-masker v1.0.9
 	github.com/go-chi/chi/v5 v5.0.7
@@ -55,7 +55,7 @@ require (
 	github.com/onsi/ginkgo/v2 v2.7.0
 	github.com/onsi/gomega v1.24.1
 	github.com/orcaman/concurrent-map v1.0.0
-	github.com/owncloud/libre-graph-api-go v1.0.2-0.20230105141655-9384face4d5d
+	github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.14.0
 	github.com/rs/zerolog v1.28.0
diff --git a/go.sum b/go.sum
index 9430715e763..15e4db225c0 100644
--- a/go.sum
+++ b/go.sum
@@ -344,8 +344,8 @@ github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3p
 github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A=
 github.com/crewjam/saml v0.4.9 h1:X2jDv4dv3IvfT9t+RhADavzNFAcq3fVxzTCIH3G605U=
 github.com/crewjam/saml v0.4.9/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4=
-github.com/cs3org/reva/v2 v2.12.1-0.20230113095557-1a7bb8c77ed0 h1:OOFyGTaCOKo3uXFNNtUAQVSNYNDKlZ21EqdDLMHUzQQ=
-github.com/cs3org/reva/v2 v2.12.1-0.20230113095557-1a7bb8c77ed0/go.mod h1:u73Df9JAZsDj43GIjQIb3DO1PLJuPutZXkRqQH0oGXA=
+github.com/cs3org/reva/v2 v2.12.1-0.20230125142028-2d125a5354d0 h1:FCKt9i38oNgs6oQYpdWaIj7KfJykCNer22HwTUi+h3Y=
+github.com/cs3org/reva/v2 v2.12.1-0.20230125142028-2d125a5354d0/go.mod h1:u73Df9JAZsDj43GIjQIb3DO1PLJuPutZXkRqQH0oGXA=
 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=
@@ -1058,8 +1058,8 @@ github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35uk
 github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY=
 github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
 github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
-github.com/owncloud/libre-graph-api-go v1.0.2-0.20230105141655-9384face4d5d h1:aqVf2yJEdSgFQd3k5fnwtYxjTwC/UREAKTZIzeupwHg=
-github.com/owncloud/libre-graph-api-go v1.0.2-0.20230105141655-9384face4d5d/go.mod h1:iKdVH6nYpI8RBeK9sjeLfzrPByST6r9d+NG2IJHoJmU=
+github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc h1:FS5FC8kLv1RSEOKkZJ0Dxr1QJtv0Q18FaPXSKUsVnAc=
+github.com/owncloud/libre-graph-api-go v1.0.2-0.20230119095249-5688a1b749dc/go.mod h1:iKdVH6nYpI8RBeK9sjeLfzrPByST6r9d+NG2IJHoJmU=
 github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
 github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
diff --git a/services/graph/pkg/identity/backend.go b/services/graph/pkg/identity/backend.go
index 5b8d5f5f444..440909f3b7a 100644
--- a/services/graph/pkg/identity/backend.go
+++ b/services/graph/pkg/identity/backend.go
@@ -70,6 +70,8 @@ type EducationBackend interface {
 	DeleteEducationClass(ctx context.Context, nameOrID string) error
 	// GetEducationClassMembers returns the EducationUser members for an EducationClass
 	GetEducationClassMembers(ctx context.Context, nameOrID string) ([]*libregraph.EducationUser, error)
+	// UpdateEducationClass updates properties of the supplied class in the identity backend.
+	UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error)
 
 	// CreateEducationUser creates a given education user in the identity backend.
 	CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error)
diff --git a/services/graph/pkg/identity/cs3.go b/services/graph/pkg/identity/cs3.go
index 6aa8043792c..2a3335d3ce6 100644
--- a/services/graph/pkg/identity/cs3.go
+++ b/services/graph/pkg/identity/cs3.go
@@ -207,10 +207,8 @@ func createGroupModelFromCS3(g *cs3group.Group) *libregraph.Group {
 		g.Id = &cs3group.GroupId{}
 	}
 	return &libregraph.Group{
-		Id:                       &g.Id.OpaqueId,
-		OnPremisesDomainName:     &g.Id.Idp,
-		OnPremisesSamAccountName: &g.GroupName,
-		DisplayName:              &g.DisplayName,
+		Id:          &g.Id.OpaqueId,
+		DisplayName: &g.GroupName,
 		// TODO when to fetch and expand memberof, usernames or ids?
 	}
 }
diff --git a/services/graph/pkg/identity/err_education.go b/services/graph/pkg/identity/err_education.go
index 6e81f7c9c58..b18080d51f2 100644
--- a/services/graph/pkg/identity/err_education.go
+++ b/services/graph/pkg/identity/err_education.go
@@ -75,6 +75,11 @@ func (i *ErrEducationBackend) GetEducationClassMembers(ctx context.Context, name
 	return nil, errNotImplemented
 }
 
+// UpdateEducationClass implments the EducationBackend interface
+func (i *ErrEducationBackend) UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) {
+	return nil, errNotImplemented
+}
+
 // CreateEducationUser creates a given education user in the identity backend.
 func (i *ErrEducationBackend) CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
 	return nil, errNotImplemented
diff --git a/services/graph/pkg/identity/ldap_education_class.go b/services/graph/pkg/identity/ldap_education_class.go
index 93ba9b13764..f19af096350 100644
--- a/services/graph/pkg/identity/ldap_education_class.go
+++ b/services/graph/pkg/identity/ldap_education_class.go
@@ -2,6 +2,7 @@ package identity
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net/url"
 
@@ -139,6 +140,98 @@ func (i *LDAP) DeleteEducationClass(ctx context.Context, id string) error {
 	return nil
 }
 
+// UpdateEducationClass implements the EducationBackend interface for the LDAP backend.
+// Only the displayName and externalID are supported to change at this point.
+func (i *LDAP) UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) {
+	logger := i.logger.SubloggerWithRequestID(ctx)
+	logger.Debug().Str("backend", "ldap").Msg("UpdateEducationClass")
+	if !i.writeEnabled {
+		return nil, ErrReadOnly
+	}
+
+	g, err := i.getLDAPGroupByID(id, false)
+	if err != nil {
+		return nil, err
+	}
+
+	var updateNeeded bool
+
+	if class.GetId() != "" {
+		if g.GetEqualFoldAttributeValue(i.groupAttributeMap.id) != class.GetId() {
+			return nil, errorcode.New(errorcode.NotAllowed, "changing the GroupID is not allowed")
+		}
+	}
+
+	if class.GetDescription() != "" {
+		return nil, errorcode.New(errorcode.NotSupported, "changing the description is currently not supported")
+	}
+
+	if len(class.GetMembers()) != 0 {
+		return nil, errorcode.New(errorcode.NotSupported, "changing the members is currently not supported")
+	}
+
+	if class.GetClassification() != "" {
+		return nil, errorcode.New(errorcode.NotSupported, "changing the classification is currently not supported")
+	}
+
+	dn := g.DN
+
+	if eID := class.GetExternalId(); eID != "" {
+		if g.GetEqualFoldAttributeValue(i.educationConfig.classAttributeMap.externalID) != eID {
+			dn, err = i.updateClassExternalID(ctx, dn, eID)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	mr := ldap.ModifyRequest{DN: dn}
+
+	if dName := class.GetDisplayName(); dName != "" {
+		if g.GetEqualFoldAttributeValue(i.groupAttributeMap.name) != dName {
+			mr.Replace(i.groupAttributeMap.name, []string{dName})
+			updateNeeded = true
+		}
+	}
+
+	if updateNeeded {
+		if err := i.conn.Modify(&mr); err != nil {
+			return nil, err
+		}
+	}
+
+	g, err = i.getEducationClassByDN(dn)
+	if err != nil {
+		return nil, err
+	}
+
+	return i.createEducationClassModelFromLDAP(g), nil
+}
+
+func (i *LDAP) updateClassExternalID(ctx context.Context, dn, externalID string) (string, error) {
+	logger := i.logger.SubloggerWithRequestID(ctx)
+	newDN := fmt.Sprintf("ocEducationExternalId=%s", externalID)
+
+	mrdn := ldap.NewModifyDNRequest(dn, newDN, true, "")
+	i.logger.Debug().Str("Backend", "ldap").
+		Str("dn", mrdn.DN).
+		Str("newrdn", mrdn.NewRDN).
+		Msg("updating class external ID")
+
+	if err := i.conn.ModifyDN(mrdn); err != nil {
+		var lerr *ldap.Error
+		logger.Debug().Err(err).Msg("error updating class external ID")
+		if errors.As(err, &lerr) {
+			if lerr.ResultCode == ldap.LDAPResultEntryAlreadyExists {
+				err = errorcode.New(errorcode.NameAlreadyExists, lerr.Error())
+			}
+		}
+		return "", err
+	}
+
+	return fmt.Sprintf("%s,%s", newDN, i.groupBaseDN), nil
+}
+
 // GetEducationClassMembers implements the EducationBackend interface for the LDAP backend.
 func (i *LDAP) GetEducationClassMembers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
 	logger := i.logger.SubloggerWithRequestID(ctx)
diff --git a/services/graph/pkg/identity/ldap_education_school.go b/services/graph/pkg/identity/ldap_education_school.go
index 77e1252ab61..6be681fc05c 100644
--- a/services/graph/pkg/identity/ldap_education_school.go
+++ b/services/graph/pkg/identity/ldap_education_school.go
@@ -348,11 +348,11 @@ func (i *LDAP) GetEducationSchools(ctx context.Context, queryParam url.Values) (
 }
 
 // GetEducationSchoolUsers implements the EducationBackend interface for the LDAP backend.
-func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
+func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, schoolNumberOrID string) ([]*libregraph.EducationUser, error) {
 	logger := i.logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "ldap").Msg("GetEducationSchoolUsers")
 
-	schoolEntry, err := i.getSchoolByNumberOrID(id)
+	schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
 	if err != nil {
 		return nil, err
 	}
@@ -360,8 +360,9 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, id string) ([]*libre
 	if schoolEntry == nil {
 		return nil, ErrNotFound
 	}
-	id = ldap.EscapeFilter(id)
-	idFilter := fmt.Sprintf("(%s=%s)", i.educationConfig.memberOfSchoolAttribute, id)
+	schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
+	schoolID = ldap.EscapeFilter(schoolID)
+	idFilter := fmt.Sprintf("(%s=%s)", i.educationConfig.memberOfSchoolAttribute, schoolID)
 	userFilter := fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.educationConfig.userObjectClass, idFilter)
 
 	searchRequest := ldap.NewSearchRequest(
@@ -398,11 +399,11 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, id string) ([]*libre
 }
 
 // AddUsersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
-func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberIDs []string) error {
+func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
 	logger := i.logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "ldap").Msg("AddUsersToEducationSchool")
 
-	schoolEntry, err := i.getSchoolByNumberOrID(schoolID)
+	schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
 	if err != nil {
 		return err
 	}
@@ -411,6 +412,8 @@ func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolID string, m
 		return ErrNotFound
 	}
 
+	schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
+
 	userEntries := make([]*ldap.Entry, 0, len(memberIDs))
 	for _, memberID := range memberIDs {
 		user, err := i.getEducationUserByNameOrID(memberID)
@@ -443,11 +446,11 @@ func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolID string, m
 }
 
 // RemoveUserFromEducationSchool removes a single member (by ID) from a school
-func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error {
+func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
 	logger := i.logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "ldap").Msg("RemoveUserFromEducationSchool")
 
-	schoolEntry, err := i.getSchoolByNumberOrID(schoolID)
+	schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
 	if err != nil {
 		return err
 	}
@@ -455,6 +458,8 @@ func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolID strin
 	if schoolEntry == nil {
 		return ErrNotFound
 	}
+
+	schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
 	user, err := i.getEducationUserByNameOrID(memberID)
 	if err != nil {
 		i.logger.Warn().Str("userid", memberID).Msg("User does not exist")
diff --git a/services/graph/pkg/identity/mocks/education_backend.go b/services/graph/pkg/identity/mocks/education_backend.go
index 2c2b4868b72..cb827811682 100644
--- a/services/graph/pkg/identity/mocks/education_backend.go
+++ b/services/graph/pkg/identity/mocks/education_backend.go
@@ -340,6 +340,29 @@ func (_m *EducationBackend) RemoveUserFromEducationSchool(ctx context.Context, s
 	return r0
 }
 
+// UpdateEducationClass provides a mock function with given fields: ctx, id, class
+func (_m *EducationBackend) UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) {
+	ret := _m.Called(ctx, id, class)
+
+	var r0 *libregraph.EducationClass
+	if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.EducationClass) *libregraph.EducationClass); ok {
+		r0 = rf(ctx, id, class)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*libregraph.EducationClass)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string, libregraph.EducationClass) error); ok {
+		r1 = rf(ctx, id, class)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // UpdateEducationSchool provides a mock function with given fields: ctx, numberOrID, school
 func (_m *EducationBackend) UpdateEducationSchool(ctx context.Context, numberOrID string, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) {
 	ret := _m.Called(ctx, numberOrID, school)
diff --git a/services/graph/pkg/service/v0/educationclasses.go b/services/graph/pkg/service/v0/educationclasses.go
index d50a1768eec..330a8ef2700 100644
--- a/services/graph/pkg/service/v0/educationclasses.go
+++ b/services/graph/pkg/service/v0/educationclasses.go
@@ -10,6 +10,8 @@ import (
 	"strings"
 
 	"github.com/CiscoM31/godata"
+	revactx "github.com/cs3org/reva/v2/pkg/ctx"
+	"github.com/cs3org/reva/v2/pkg/events"
 	libregraph "github.com/owncloud/libre-graph-api-go"
 	"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
 
@@ -120,6 +122,25 @@ func (g Graph) PatchEducationClass(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	var features []events.GroupFeature
+	if displayName, ok := changes.GetDisplayNameOk(); ok {
+		features = append(features, events.GroupFeature{Name: "displayname", Value: *displayName})
+	}
+
+	if externalID, ok := changes.GetExternalIdOk(); ok {
+		features = append(features, events.GroupFeature{Name: "externalid", Value: *externalID})
+	}
+
+	_, err = g.identityEducationBackend.UpdateEducationClass(r.Context(), classID, *changes)
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Str("classID", classID).
+			Msg("could not update class")
+		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
+		return
+	}
+
 	if memberRefs, ok := changes.GetMembersodataBindOk(); ok {
 		// The spec defines a limit of 20 members maxium per Request
 		if len(memberRefs) > g.config.API.GroupMembersPatchLimit {
@@ -166,6 +187,20 @@ func (g Graph) PatchEducationClass(w http.ResponseWriter, r *http.Request) {
 		}
 		return
 	}
+
+	if len(features) > 0 {
+		e := events.GroupFeatureChanged{
+			GroupID:  classID,
+			Features: features,
+		}
+
+		if currentUser, ok := revactx.ContextGetUser(r.Context()); ok {
+			e.Executant = currentUser.GetId()
+		}
+		g.publishEvent(e)
+
+	}
+
 	render.Status(r, http.StatusNoContent) // TODO StatusNoContent when prefer=minimal is used, otherwise OK and the resource in the body
 	render.NoContent(w, r)
 }
diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go
index 8ce2d93a243..bfc287b4fc4 100644
--- a/services/graph/pkg/service/v0/service.go
+++ b/services/graph/pkg/service/v0/service.go
@@ -270,6 +270,11 @@ func NewService(opts ...Option) (Graph, error) {
 		})
 	})
 
+	_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	return svc, nil
 }
 
diff --git a/services/graph/pkg/service/v0/users.go b/services/graph/pkg/service/v0/users.go
index c0bddc77752..09ea4512bbf 100644
--- a/services/graph/pkg/service/v0/users.go
+++ b/services/graph/pkg/service/v0/users.go
@@ -188,10 +188,6 @@ func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
 			errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("%v is not a valid email address", *u.Mail))
 			return
 		}
-	} else {
-		logger.Debug().Interface("user", u).Msg("could not create user: missing required Attribute: 'mail'")
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'mail'")
-		return
 	}
 
 	// Disallow user-supplied IDs. It's supposed to be readonly. We're either
@@ -273,7 +269,20 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
 	}
 	sel := strings.Split(r.URL.Query().Get("$select"), ",")
 	exp := strings.Split(r.URL.Query().Get("$expand"), ",")
-	if slices.Contains(sel, "drive") || slices.Contains(sel, "drives") || slices.Contains(exp, "drive") || slices.Contains(exp, "drives") {
+	listDrives := slices.Contains(sel, "drives") || slices.Contains(exp, "drives")
+	listDrive := slices.Contains(sel, "drive") || slices.Contains(exp, "drive")
+
+	// do we need to list all or only the personal drive
+	filters := []*storageprovider.ListStorageSpacesRequest_Filter{}
+	if listDrives || listDrive {
+		filters = append(filters, listStorageSpacesUserFilter(user.GetId()))
+		if !listDrives {
+			// TODO filter by owner when decomposedfs supports the OWNER filter
+			filters = append(filters, listStorageSpacesTypeFilter("personal"))
+		}
+	}
+
+	if len(filters) > 0 {
 		wdu, err := g.getWebDavBaseURL()
 		if err != nil {
 			// log error, wrong configuration
@@ -285,14 +294,13 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
 			render.Status(r, http.StatusInternalServerError)
 			return
 		}
-		logger.Debug().Str("id", user.GetId()).Msg("calling list storage spaces with user id filter")
-		f := listStorageSpacesUserFilter(user.GetId())
+		logger.Debug().Str("id", user.GetId()).Msg("calling list storage spaces with filter")
 		// use the unrestricted flag to get all possible spaces
 		// users with the canListAllSpaces permission should see all spaces
 		opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T")
 		lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{
 			Opaque:  opaque,
-			Filters: []*storageprovider.ListStorageSpacesRequest_Filter{f},
+			Filters: filters,
 		})
 		if err != nil {
 			// transport error, needs to be fixed by admin
@@ -302,13 +310,18 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 		if lspr.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
-			logger.Debug().Str("grpc", lspr.GetStatus().GetMessage()).Msg("could not get drive for user")
+			logger.Debug().Str("grpc", lspr.GetStatus().GetMessage()).Msg("could not list drives for user")
 			// in case of NOT_OK, we can just return the user object with empty drives
 			render.Status(r, status.HTTPStatusFromCode(http.StatusOK))
 			render.JSON(w, r, user)
 			return
 		}
-		drives := []libregraph.Drive{}
+		if listDrives {
+			user.Drives = make([]libregraph.Drive, 0, len(lspr.GetStorageSpaces()))
+		}
+		if listDrive {
+			user.Drive = &libregraph.Drive{}
+		}
 		for _, sp := range lspr.GetStorageSpaces() {
 			d, err := g.cs3StorageSpaceToDrive(r.Context(), wdu, sp)
 			if err != nil {
@@ -320,16 +333,18 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
 				logger.Debug().Err(err).Interface("id", sp.Id).Msg("error calling get quota on drive")
 			}
 			d.Quota = &quota
-			if slices.Contains(sel, "drive") || slices.Contains(exp, "drive") {
-				if *d.DriveType == "personal" {
+			if d.GetDriveType() == "personal" && sp.GetOwner().GetId().GetOpaqueId() == user.GetId() {
+				if listDrive {
 					user.Drive = d
 				}
 			} else {
-				drives = append(drives, *d)
-				user.Drives = drives
+				if listDrives {
+					user.Drives = append(user.Drives, *d)
+				}
 			}
 		}
 	}
+
 	// expand appRoleAssignments if requested
 	if slices.Contains(exp, "appRoleAssignments") {
 		user.AppRoleAssignments, err = g.fetchAppRoleAssignments(r.Context(), user.GetId())
diff --git a/services/idp/pkg/service/v0/service.go b/services/idp/pkg/service/v0/service.go
index 82b8d41277c..ca60923eff7 100644
--- a/services/idp/pkg/service/v0/service.go
+++ b/services/idp/pkg/service/v0/service.go
@@ -238,6 +238,11 @@ func (idp *IDP) initMux(ctx context.Context, r []server.WithRoutes, h http.Handl
 	idp.mux.Get("/signin/v1/identifier/index.html", idp.Index())
 
 	idp.mux.Mount("/", gm)
+
+	_ = chi.Walk(idp.mux, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
 }
 
 // ServeHTTP implements the Service interface.
diff --git a/services/ocs/pkg/command/server.go b/services/ocs/pkg/command/server.go
index 6b6ab26a463..7c3b6c989b4 100644
--- a/services/ocs/pkg/command/server.go
+++ b/services/ocs/pkg/command/server.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
-	ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
 	"github.com/owncloud/ocis/v2/ocis-pkg/version"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/config/parser"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/logging"
@@ -34,10 +33,6 @@ func Server(cfg *config.Config) *cli.Command {
 			if err != nil {
 				return err
 			}
-			err = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
-			if err != nil {
-				return err
-			}
 
 			var (
 				gr          = run.Group{}
diff --git a/services/ocs/pkg/config/config.go b/services/ocs/pkg/config/config.go
index e4130405447..1de8f60aa3e 100644
--- a/services/ocs/pkg/config/config.go
+++ b/services/ocs/pkg/config/config.go
@@ -19,21 +19,7 @@ type Config struct {
 
 	HTTP HTTP `yaml:"http"`
 
-	TokenManager  *TokenManager         `yaml:"token_manager"`
-	Reva          *shared.Reva          `yaml:"reva"`
-	GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
-
-	IdentityManagement IdentityManagement `yaml:"identity_management"`
-
-	AccountBackend    string `yaml:"-"` // we only support cs3 backend, no need to have this configurable
-	MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;OCS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
+	TokenManager *TokenManager `yaml:"token_manager"`
 
 	Context context.Context `yaml:"-"`
 }
-
-// IdentityManagement keeps track of the OIDC address. This is because Reva requisite of uniqueness for users
-// is based in the combination of IDP hostname + UserID. For more information see:
-// https://github.com/cs3org/reva/blob/4fd0229f13fae5bc9684556a82dbbd0eced65ef9/pkg/storage/utils/decomposedfs/node/node.go#L856-L865
-type IdentityManagement struct {
-	Address string `yaml:"address" env:"OCIS_URL;OCIS_OIDC_ISSUER;OCS_IDM_ADDRESS" desc:"URL of the OIDC issuer. It defaults to URL of the builtin IDP."`
-}
diff --git a/services/ocs/pkg/config/defaults/defaultconfig.go b/services/ocs/pkg/config/defaults/defaultconfig.go
index 30dc68871b0..9b04fb99f8b 100644
--- a/services/ocs/pkg/config/defaults/defaultconfig.go
+++ b/services/ocs/pkg/config/defaults/defaultconfig.go
@@ -3,7 +3,6 @@ package defaults
 import (
 	"strings"
 
-	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
 )
 
@@ -36,11 +35,6 @@ func DefaultConfig() *config.Config {
 		Service: config.Service{
 			Name: "ocs",
 		},
-		AccountBackend: "cs3",
-		Reva:           shared.DefaultRevaConfig(),
-		IdentityManagement: config.IdentityManagement{
-			Address: "https://localhost:9200",
-		},
 	}
 }
 
@@ -78,15 +72,6 @@ func EnsureDefaults(cfg *config.Config) {
 		cfg.CacheStore = &config.CacheStore{}
 	}
 
-	if cfg.Reva == nil && cfg.Commons != nil && cfg.Commons.Reva != nil {
-		cfg.Reva = &shared.Reva{
-			Address: cfg.Commons.Reva.Address,
-			TLS:     cfg.Commons.Reva.TLS,
-		}
-	} else if cfg.Reva == nil {
-		cfg.Reva = &shared.Reva{}
-	}
-
 	if cfg.TokenManager == nil && cfg.Commons != nil && cfg.Commons.TokenManager != nil {
 		cfg.TokenManager = &config.TokenManager{
 			JWTSecret: cfg.Commons.TokenManager.JWTSecret,
@@ -95,17 +80,6 @@ func EnsureDefaults(cfg *config.Config) {
 		cfg.TokenManager = &config.TokenManager{}
 	}
 
-	if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
-		cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
-	}
-
-	if cfg.GRPCClientTLS == nil {
-		cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
-		if cfg.Commons != nil && cfg.Commons.GRPCClientTLS != nil {
-			cfg.GRPCClientTLS.Mode = cfg.Commons.GRPCClientTLS.Mode
-			cfg.GRPCClientTLS.CACert = cfg.Commons.GRPCClientTLS.CACert
-		}
-	}
 	if cfg.Commons != nil {
 		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
 	}
diff --git a/services/ocs/pkg/config/parser/parse.go b/services/ocs/pkg/config/parser/parse.go
index ecb6b0e184b..bff72f8e7da 100644
--- a/services/ocs/pkg/config/parser/parse.go
+++ b/services/ocs/pkg/config/parser/parse.go
@@ -39,9 +39,5 @@ func Validate(cfg *config.Config) error {
 		return shared.MissingJWTTokenError(cfg.Service.Name)
 	}
 
-	if cfg.MachineAuthAPIKey == "" {
-		return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
-	}
-
 	return nil
 }
diff --git a/services/ocs/pkg/middleware/options.go b/services/ocs/pkg/middleware/options.go
deleted file mode 100644
index c3f58e51f3a..00000000000
--- a/services/ocs/pkg/middleware/options.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package middleware
-
-import (
-	"github.com/owncloud/ocis/v2/ocis-pkg/log"
-	"github.com/owncloud/ocis/v2/ocis-pkg/roles"
-)
-
-// Option defines a single option function.
-type Option func(o *Options)
-
-// Options defines the available options for this package.
-type Options struct {
-	// Logger to use for logging, must be set
-	Logger log.Logger
-	// RoleManager for looking up permissions
-	RoleManager *roles.Manager
-}
-
-// newOptions initializes the available default options.
-func newOptions(opts ...Option) Options {
-	opt := Options{}
-
-	for _, o := range opts {
-		o(&opt)
-	}
-
-	return opt
-}
-
-// Logger provides a function to set the logger option.
-func Logger(l log.Logger) Option {
-	return func(o *Options) {
-		o.Logger = l
-	}
-}
-
-// RoleManager provides a function to set the RoleManager option.
-func RoleManager(val *roles.Manager) Option {
-	return func(o *Options) {
-		o.RoleManager = val
-	}
-}
diff --git a/services/ocs/pkg/middleware/requireadmin.go b/services/ocs/pkg/middleware/requireadmin.go
deleted file mode 100644
index 87f58eebe09..00000000000
--- a/services/ocs/pkg/middleware/requireadmin.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package middleware
-
-import (
-	"net/http"
-
-	"github.com/go-chi/render"
-	"github.com/owncloud/ocis/v2/ocis-pkg/roles"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-	settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
-)
-
-// RequireAdmin middleware is used to require the user in context to be an admin / have account management permissions
-func RequireAdmin(opts ...Option) func(next http.Handler) http.Handler {
-	opt := newOptions(opts...)
-
-	mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
-		if err := render.Render(w, r, renderer); err != nil {
-			opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
-		}
-	}
-
-	return func(next http.Handler) http.Handler {
-		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-			// get roles from context
-			roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context())
-			if !ok {
-				mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-				return
-			}
-
-			// check if permission is present in roles of the authenticated account
-			if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.AccountManagementPermissionID) != nil {
-				next.ServeHTTP(w, r)
-				return
-			}
-
-			mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-		})
-	}
-}
diff --git a/services/ocs/pkg/middleware/requireselforadmin.go b/services/ocs/pkg/middleware/requireselforadmin.go
deleted file mode 100644
index f88863db5e5..00000000000
--- a/services/ocs/pkg/middleware/requireselforadmin.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package middleware
-
-import (
-	"net/http"
-	"net/url"
-
-	revactx "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/go-chi/chi/v5"
-	"github.com/go-chi/render"
-	"github.com/owncloud/ocis/v2/ocis-pkg/roles"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-	settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
-)
-
-// RequireSelfOrAdmin middleware is used to require the requesting user to be an admin or the requested user himself
-func RequireSelfOrAdmin(opts ...Option) func(next http.Handler) http.Handler {
-	opt := newOptions(opts...)
-
-	mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
-		if err := render.Render(w, r, renderer); err != nil {
-			opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
-		}
-	}
-
-	return func(next http.Handler) http.Handler {
-		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-			u, ok := revactx.ContextGetUser(r.Context())
-			if !ok {
-				mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-				return
-			}
-			if u.Id == nil || u.Id.OpaqueId == "" {
-				mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
-				return
-			}
-			// get roles from context
-			roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context())
-			if !ok {
-				opt.Logger.Debug().Str("userid", u.Id.OpaqueId).Msg("No roles in context, contacting settings service")
-				var err error
-				roleIDs, err = opt.RoleManager.FindRoleIDsForUser(r.Context(), u.Id.OpaqueId)
-				if err != nil {
-					opt.Logger.Err(err).Str("userid", u.Id.OpaqueId).Msg("failed to get roles for user")
-					mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-					return
-				}
-				if len(roleIDs) == 0 {
-					roleIDs = append(roleIDs, settings.BundleUUIDRoleUser, settings.SelfManagementPermissionID)
-					// if roles are empty, assume we haven't seen the user before and assign a default user role. At least until
-					// proper roles are provided. See https://github.com/owncloud/ocis/v2/issues/1825 for more context.
-					//return user, nil
-				}
-			}
-
-			// check if account management permission is present in roles of the authenticated account
-			if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.AccountManagementPermissionID) != nil {
-				next.ServeHTTP(w, r)
-				return
-			}
-
-			// check if self management permission is present in roles of the authenticated account
-			if opt.RoleManager.FindPermissionByID(r.Context(), roleIDs, settings.SelfManagementPermissionID) != nil {
-				userid := chi.URLParam(r, "userid")
-				var err error
-				if userid, err = url.PathUnescape(userid); err != nil {
-					mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "malformed username"))
-				}
-
-				if userid == "" || userid == u.Id.OpaqueId || userid == u.Username {
-					next.ServeHTTP(w, r)
-					return
-				}
-			}
-
-			mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-
-		})
-	}
-}
diff --git a/services/ocs/pkg/middleware/requireuser.go b/services/ocs/pkg/middleware/requireuser.go
deleted file mode 100644
index 2b17f178b04..00000000000
--- a/services/ocs/pkg/middleware/requireuser.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package middleware
-
-import (
-	"net/http"
-
-	revactx "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/go-chi/render"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-)
-
-// RequireUser middleware is used to require a user in context
-func RequireUser(opts ...Option) func(next http.Handler) http.Handler {
-	opt := newOptions(opts...)
-
-	mustRender := func(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
-		if err := render.Render(w, r, renderer); err != nil {
-			opt.Logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
-		}
-	}
-
-	return func(next http.Handler) http.Handler {
-		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-			u, ok := revactx.ContextGetUser(r.Context())
-			if !ok {
-				mustRender(w, r, response.ErrRender(data.MetaUnauthorized.StatusCode, "Unauthorized"))
-				return
-			}
-			if u.Id == nil || u.Id.OpaqueId == "" {
-				mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
-				return
-			}
-
-			next.ServeHTTP(w, r)
-
-		})
-	}
-}
diff --git a/services/ocs/pkg/service/v0/config.go b/services/ocs/pkg/service/v0/config.go
deleted file mode 100644
index 9ff28bad191..00000000000
--- a/services/ocs/pkg/service/v0/config.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package svc
-
-import (
-	"net/http"
-
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-)
-
-// GetConfig renders the ocs config endpoint
-func (o Ocs) GetConfig(w http.ResponseWriter, r *http.Request) {
-	o.mustRender(w, r, response.DataRender(&data.ConfigData{
-		Version: "1.7",  // TODO get from env
-		Website: "ocis", // TODO get from env
-		Host:    "",     // TODO get from FRONTEND config
-		Contact: "",     // TODO get from env
-		SSL:     "true", // TODO get from env
-	}))
-}
diff --git a/services/ocs/pkg/service/v0/data/config.go b/services/ocs/pkg/service/v0/data/config.go
deleted file mode 100644
index 0978b73c5f5..00000000000
--- a/services/ocs/pkg/service/v0/data/config.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package data
-
-// ConfigData holds basic config
-type ConfigData struct {
-	Version string `json:"version" xml:"version"`
-	Website string `json:"website" xml:"website"`
-	Host    string `json:"host" xml:"host"`
-	Contact string `json:"contact" xml:"contact"`
-	SSL     string `json:"ssl" xml:"ssl"`
-}
diff --git a/services/ocs/pkg/service/v0/data/group.go b/services/ocs/pkg/service/v0/data/group.go
deleted file mode 100644
index 1eab5d8bd39..00000000000
--- a/services/ocs/pkg/service/v0/data/group.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package data
-
-// Groups holds group ids for the groups listing
-type Groups struct {
-	Groups []string `json:"groups" xml:"groups>element"`
-}
diff --git a/services/ocs/pkg/service/v0/data/user.go b/services/ocs/pkg/service/v0/data/user.go
index 8b210afb570..fbb3e0d8d52 100644
--- a/services/ocs/pkg/service/v0/data/user.go
+++ b/services/ocs/pkg/service/v0/data/user.go
@@ -1,31 +1,5 @@
 package data
 
-// Users holds user ids for the user listing
-type Users struct {
-	Users []string `json:"users" xml:"users>element"`
-}
-
-// User holds the payload for a GetUser response
-type User struct {
-	Enabled           string `json:"enabled" xml:"enabled"`
-	UserID            string `json:"id" xml:"id"` // UserID is mapped to the preferred_name attribute in accounts
-	DisplayName       string `json:"display-name" xml:"display-name"`
-	LegacyDisplayName string `json:"displayname" xml:"displayname"`
-	Email             string `json:"email" xml:"email"`
-	Quota             *Quota `json:"quota" xml:"quota"`
-	UIDNumber         int64  `json:"uidnumber" xml:"uidnumber"`
-	GIDNumber         int64  `json:"gidnumber" xml:"gidnumber"`
-}
-
-// Quota holds quota information
-type Quota struct {
-	Free       int64   `json:"free" xml:"free"`
-	Used       int64   `json:"used" xml:"used"`
-	Total      int64   `json:"total" xml:"total"`
-	Relative   float32 `json:"relative" xml:"relative"`
-	Definition string  `json:"definition" xml:"definition"`
-}
-
 // SigningKey holds the Payload for a GetSigningKey response
 type SigningKey struct {
 	User       string `json:"user" xml:"user"`
diff --git a/services/ocs/pkg/service/v0/groups.go b/services/ocs/pkg/service/v0/groups.go
deleted file mode 100644
index 0f136422610..00000000000
--- a/services/ocs/pkg/service/v0/groups.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package svc
-
-import (
-	"net/http"
-
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
-	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-)
-
-const (
-	_backendCS3 = "cs3"
-)
-
-// ListUserGroups lists a users groups
-func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		// TODO
-		o.mustRender(w, r, response.DataRender(&data.Groups{}))
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// AddToGroup adds a user to a group
-func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		// TODO
-		o.cs3WriteNotSupported(w, r)
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// RemoveFromGroup removes a user from a group
-func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		// TODO
-		o.cs3WriteNotSupported(w, r)
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// ListGroups lists all groups
-func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		// TODO
-		o.mustRender(w, r, response.DataRender(&data.Groups{}))
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// AddGroup adds a group
-// oC10 implementation: https://github.com/owncloud/core/blob/762780a23c9eadda4fb5fa8db99eba66a5100b6e/apps/provisioning_api/lib/Groups.php#L126-L154
-func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		o.cs3WriteNotSupported(w, r)
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// DeleteGroup deletes a group
-func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		o.cs3WriteNotSupported(w, r)
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// GetGroupMembers lists all members of a group
-func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case _backendCS3:
-		// TODO
-		o.mustRender(w, r, response.DataRender(&data.Users{}))
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
diff --git a/services/ocs/pkg/service/v0/instrument.go b/services/ocs/pkg/service/v0/instrument.go
index 15c42b348f9..8c48325201e 100644
--- a/services/ocs/pkg/service/v0/instrument.go
+++ b/services/ocs/pkg/service/v0/instrument.go
@@ -23,8 +23,3 @@ type instrument struct {
 func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	i.next.ServeHTTP(w, r)
 }
-
-// GetConfig implements the Service interface.
-func (i instrument) GetConfig(w http.ResponseWriter, r *http.Request) {
-	i.next.GetConfig(w, r)
-}
diff --git a/services/ocs/pkg/service/v0/logging.go b/services/ocs/pkg/service/v0/logging.go
index bb22e46fccd..c4e7c8382eb 100644
--- a/services/ocs/pkg/service/v0/logging.go
+++ b/services/ocs/pkg/service/v0/logging.go
@@ -23,8 +23,3 @@ type logging struct {
 func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	l.next.ServeHTTP(w, r)
 }
-
-// GetConfig implements the Service interface.
-func (l logging) GetConfig(w http.ResponseWriter, r *http.Request) {
-	l.next.GetConfig(w, r)
-}
diff --git a/services/ocs/pkg/service/v0/option.go b/services/ocs/pkg/service/v0/option.go
index bfefbe61f09..40ccc9e1c76 100644
--- a/services/ocs/pkg/service/v0/option.go
+++ b/services/ocs/pkg/service/v0/option.go
@@ -4,8 +4,6 @@ import (
 	"net/http"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
-	"github.com/owncloud/ocis/v2/ocis-pkg/roles"
-	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
 )
 
@@ -14,11 +12,9 @@ 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
-	RoleService settingssvc.RoleService
-	RoleManager *roles.Manager
+	Logger     log.Logger
+	Config     *config.Config
+	Middleware []func(http.Handler) http.Handler
 }
 
 // newOptions initializes the available default options.
@@ -52,17 +48,3 @@ func Middleware(val ...func(http.Handler) http.Handler) Option {
 		o.Middleware = val
 	}
 }
-
-// RoleService provides a function to set the RoleService option.
-func RoleService(val settingssvc.RoleService) Option {
-	return func(o *Options) {
-		o.RoleService = val
-	}
-}
-
-// RoleManager provides a function to set the RoleManager option.
-func RoleManager(val *roles.Manager) Option {
-	return func(o *Options) {
-		o.RoleManager = val
-	}
-}
diff --git a/services/ocs/pkg/service/v0/service.go b/services/ocs/pkg/service/v0/service.go
index 60b7e2b08b3..5e333415a9f 100644
--- a/services/ocs/pkg/service/v0/service.go
+++ b/services/ocs/pkg/service/v0/service.go
@@ -3,10 +3,6 @@ package svc
 import (
 	"net/http"
 
-	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
-	"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
-	"github.com/owncloud/ocis/v2/ocis-pkg/store"
-
 	"github.com/go-chi/chi/v5"
 	"github.com/go-chi/chi/v5/middleware"
 	"github.com/go-chi/render"
@@ -14,19 +10,15 @@ import (
 	"github.com/owncloud/ocis/v2/ocis-pkg/account"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	opkgm "github.com/owncloud/ocis/v2/ocis-pkg/middleware"
-	"github.com/owncloud/ocis/v2/ocis-pkg/roles"
-	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/config"
 	ocsm "github.com/owncloud/ocis/v2/services/ocs/pkg/middleware"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-	"github.com/owncloud/ocis/v2/services/proxy/pkg/user/backend"
 )
 
 // Service defines the service handlers.
 type Service interface {
 	ServeHTTP(http.ResponseWriter, *http.Request)
-	GetConfig(http.ResponseWriter, *http.Request)
 }
 
 // NewService returns a service implementation for Service.
@@ -36,40 +28,12 @@ func NewService(opts ...Option) Service {
 	m := chi.NewMux()
 	m.Use(options.Middleware...)
 
-	roleService := options.RoleService
-	if roleService == nil {
-		roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
-	}
-	roleManager := options.RoleManager
-	if roleManager == nil {
-		storeOptions := store.OcisStoreOptions{
-			Type:    options.Config.CacheStore.Type,
-			Address: options.Config.CacheStore.Address,
-			Size:    options.Config.CacheStore.Size,
-		}
-		m := roles.NewManager(
-			roles.StoreOptions(storeOptions),
-			roles.Logger(options.Logger),
-			roles.RoleService(roleService),
-		)
-		roleManager = &m
-	}
-
 	svc := Ocs{
-		config:      options.Config,
-		mux:         m,
-		RoleManager: roleManager,
-		logger:      options.Logger,
-	}
-
-	if svc.config.AccountBackend == "" {
-		svc.config.AccountBackend = "cs3"
+		config: options.Config,
+		mux:    m,
+		logger: options.Logger,
 	}
 
-	requireUser := ocsm.RequireUser(
-		ocsm.Logger(options.Logger),
-	)
-
 	m.Route(options.Config.HTTP.Root, func(r chi.Router) {
 		r.NotFound(svc.NotFound)
 		r.Use(middleware.StripSlashes)
@@ -80,31 +44,23 @@ func NewService(opts ...Option) Service {
 		r.Use(ocsm.OCSFormatCtx) // updates request Accept header according to format=(json|xml) query parameter
 		r.Route("/v{version:(1|2)}.php", func(r chi.Router) {
 			r.Use(response.VersionCtx) // stores version in context
-			r.Route("/apps/files_sharing/api/v1", func(r chi.Router) {})
-			r.Route("/apps/notifications/api/v1", func(r chi.Router) {})
-			r.Route("/cloud", func(r chi.Router) {
-				r.Route("/capabilities", func(r chi.Router) {})
-				// TODO /apps
-				r.Route("/user", func(r chi.Router) {
-					r.Get("/signing-key", svc.GetSigningKey)
-				})
-			})
-			r.Route("/config", func(r chi.Router) {
-				r.With(requireUser).Get("/", svc.GetConfig)
-			})
+			r.Get("/cloud/user/signing-key", svc.GetSigningKey)
 		})
 	})
 
+	_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	return svc
 }
 
 // Ocs defines implements the business logic for Service.
 type Ocs struct {
-	config      *config.Config
-	logger      log.Logger
-	RoleService settingssvc.RoleService
-	RoleManager *roles.Manager
-	mux         *chi.Mux
+	config *config.Config
+	logger log.Logger
+	mux    *chi.Mux
 }
 
 // ServeHTTP implements the Service interface.
@@ -117,19 +73,6 @@ func (o Ocs) NotFound(w http.ResponseWriter, r *http.Request) {
 	o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found"))
 }
 
-func (o Ocs) getCS3Backend() backend.UserBackend {
-	revaClient, err := pool.GetGatewayServiceClient(o.config.Reva.Address, o.config.Reva.GetRevaOptions()...)
-	if err != nil {
-		o.logger.Fatal().Msgf("could not get reva client at address %s", o.config.Reva.Address)
-	}
-	return backend.NewCS3UserBackend(nil, revaClient, o.config.MachineAuthAPIKey, "", nil, o.logger)
-}
-
-// NotImplementedStub returns a not implemented error
-func (o Ocs) NotImplementedStub(w http.ResponseWriter, r *http.Request) {
-	o.mustRender(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "Not implemented"))
-}
-
 func (o Ocs) mustRender(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
 	if err := render.Render(w, r, renderer); err != nil {
 		o.logger.Err(err).Msgf("failed to write response for ocs request %s on %s", r.Method, r.URL)
diff --git a/services/ocs/pkg/service/v0/tracing.go b/services/ocs/pkg/service/v0/tracing.go
index 6c807d772db..4a2c04f51fa 100644
--- a/services/ocs/pkg/service/v0/tracing.go
+++ b/services/ocs/pkg/service/v0/tracing.go
@@ -21,8 +21,3 @@ type tracing struct {
 func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	middleware.TraceContext(t.next).ServeHTTP(w, r)
 }
-
-// GetConfig implements the Service interface.
-func (t tracing) GetConfig(w http.ResponseWriter, r *http.Request) {
-	t.next.GetConfig(w, r)
-}
diff --git a/services/ocs/pkg/service/v0/users.go b/services/ocs/pkg/service/v0/users.go
index df788d91034..f0bb5c742d3 100644
--- a/services/ocs/pkg/service/v0/users.go
+++ b/services/ocs/pkg/service/v0/users.go
@@ -1,155 +1,20 @@
 package svc
 
 import (
-	"context"
 	"crypto/rand"
 	"encoding/hex"
 	"net/http"
-	"net/url"
 
 	storemsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/store/v0"
 	storesvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/store/v0"
 
-	cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	revactx "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/go-chi/chi/v5"
 	"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/data"
 	"github.com/owncloud/ocis/v2/services/ocs/pkg/service/v0/response"
-	ocstracing "github.com/owncloud/ocis/v2/services/ocs/pkg/tracing"
 	merrors "go-micro.dev/v4/errors"
 )
 
-// GetSelf returns the currently logged in user
-func (o Ocs) GetSelf(w http.ResponseWriter, r *http.Request) {
-	u, ok := revactx.ContextGetUser(r.Context())
-	if !ok || u.Id == nil || u.Id.OpaqueId == "" {
-		o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "user is missing an id"))
-		return
-	}
-	d := &data.User{
-		UserID:            u.Username,
-		DisplayName:       u.DisplayName,
-		LegacyDisplayName: u.DisplayName,
-		Email:             u.Mail,
-		UIDNumber:         u.UidNumber,
-		GIDNumber:         u.GidNumber,
-	}
-	o.mustRender(w, r, response.DataRender(d))
-}
-
-// GetUser returns the user with the given userid
-func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
-	userid := chi.URLParam(r, "userid")
-	userid, err := url.PathUnescape(userid)
-	if err != nil {
-		o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
-	}
-
-	var user *cs3.User
-	switch {
-	case userid == "":
-		o.mustRender(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
-	case o.config.AccountBackend == "cs3":
-		user, err = o.fetchAccountFromCS3Backend(r.Context(), userid)
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-
-	if err != nil {
-		merr := merrors.FromError(err)
-		if merr.Code == http.StatusNotFound {
-			o.mustRender(w, r, response.ErrRender(data.MetaNotFound.StatusCode, data.MessageUserNotFound))
-		} else {
-			o.mustRender(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
-		}
-		o.logger.Error().Err(merr).Str("userid", userid).Msg("could not get account for user")
-		return
-	}
-
-	o.logger.Debug().Interface("user", user).Msg("got user")
-
-	d := &data.User{
-		UserID:            user.Username,
-		DisplayName:       user.DisplayName,
-		LegacyDisplayName: user.DisplayName,
-		Email:             user.Mail,
-		UIDNumber:         user.UidNumber,
-		GIDNumber:         user.GidNumber,
-		Enabled:           "true", // TODO include in response only when admin?
-		// TODO query storage registry for free space? of home storage, maybe...
-		Quota: &data.Quota{
-			Free:       2840756224000,
-			Used:       5059416668,
-			Total:      2845815640668,
-			Relative:   0.18,
-			Definition: "default",
-		},
-	}
-
-	_, span := ocstracing.TraceProvider.
-		Tracer("ocs").
-		Start(r.Context(), "GetUser")
-	defer span.End()
-
-	o.mustRender(w, r, response.DataRender(d))
-}
-
-// AddUser creates a new user account
-func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// EditUser creates a new user account
-func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// DeleteUser deletes a user
-func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// EnableUser enables a user
-func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-// DisableUser disables a user
-func (o Ocs) DisableUser(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
 // GetSigningKey returns the signing key for the current user. It will create it on the fly if it does not exist
 // The signing key is part of the user settings and is used by the proxy to authenticate requests
 // Currently, the username is used as the OC-Credential
@@ -222,29 +87,3 @@ func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
 		SigningKey: signingKey,
 	}))
 }
-
-// ListUsers lists the users
-func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
-	switch o.config.AccountBackend {
-	case "cs3":
-		// TODO
-		o.cs3WriteNotSupported(w, r)
-		return
-	default:
-		o.logger.Fatal().Msgf("Invalid accounts backend type '%s'", o.config.AccountBackend)
-	}
-}
-
-func (o Ocs) fetchAccountFromCS3Backend(ctx context.Context, name string) (*cs3.User, error) {
-	backend := o.getCS3Backend()
-	u, _, err := backend.GetUserByClaims(ctx, "username", name, false)
-	if err != nil {
-		return nil, err
-	}
-	return u, nil
-}
-
-func (o Ocs) cs3WriteNotSupported(w http.ResponseWriter, r *http.Request) {
-	o.logger.Warn().Msg("the CS3 backend does not support adding or updating users")
-	o.NotImplementedStub(w, r)
-}
diff --git a/services/search/pkg/content/mocks/extractor.go b/services/search/pkg/content/mocks/extractor.go
index d1ab8d08188..d3f705014b5 100644
--- a/services/search/pkg/content/mocks/extractor.go
+++ b/services/search/pkg/content/mocks/extractor.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.14.1. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/search/pkg/content/mocks/retriever.go b/services/search/pkg/content/mocks/retriever.go
index 9b50186c64b..bc380c7e690 100644
--- a/services/search/pkg/content/mocks/retriever.go
+++ b/services/search/pkg/content/mocks/retriever.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.14.1. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/search/pkg/engine/mocks/engine.go b/services/search/pkg/engine/mocks/engine.go
index 61037adc514..273fd2e93be 100644
--- a/services/search/pkg/engine/mocks/engine.go
+++ b/services/search/pkg/engine/mocks/engine.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.14.1. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/search/pkg/search/mocks/searcher.go b/services/search/pkg/search/mocks/searcher.go
index 2f94ad0ad1b..557e9a0e5aa 100644
--- a/services/search/pkg/search/mocks/searcher.go
+++ b/services/search/pkg/search/mocks/searcher.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.14.1. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/settings/pkg/server/http/server.go b/services/settings/pkg/server/http/server.go
index fd92344ad17..2a7813036ac 100644
--- a/services/settings/pkg/server/http/server.go
+++ b/services/settings/pkg/server/http/server.go
@@ -2,13 +2,14 @@ package http
 
 import (
 	"fmt"
+	"net/http"
 
 	"github.com/go-chi/chi/v5"
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/account"
 	"github.com/owncloud/ocis/v2/ocis-pkg/cors"
 	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
-	"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
+	ohttp "github.com/owncloud/ocis/v2/ocis-pkg/service/http"
 	"github.com/owncloud/ocis/v2/ocis-pkg/version"
 	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
 	"github.com/owncloud/ocis/v2/services/settings/pkg/assets"
@@ -17,24 +18,24 @@ import (
 )
 
 // Server initializes the http service and server.
-func Server(opts ...Option) (http.Service, error) {
+func Server(opts ...Option) (ohttp.Service, error) {
 	options := newOptions(opts...)
 
-	service, err := http.NewService(
-		http.TLSConfig(options.Config.HTTP.TLS),
-		http.Logger(options.Logger),
-		http.Name(options.Name),
-		http.Version(version.GetString()),
-		http.Address(options.Config.HTTP.Addr),
-		http.Namespace(options.Config.HTTP.Namespace),
-		http.Context(options.Context),
-		http.Flags(options.Flags...),
+	service, err := ohttp.NewService(
+		ohttp.TLSConfig(options.Config.HTTP.TLS),
+		ohttp.Logger(options.Logger),
+		ohttp.Name(options.Name),
+		ohttp.Version(version.GetString()),
+		ohttp.Address(options.Config.HTTP.Addr),
+		ohttp.Namespace(options.Config.HTTP.Namespace),
+		ohttp.Context(options.Context),
+		ohttp.Flags(options.Flags...),
 	)
 	if err != nil {
 		options.Logger.Error().
 			Err(err).
 			Msg("Error initializing http service")
-		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+		return ohttp.Service{}, fmt.Errorf("could not initialize http service: %w", err)
 	}
 
 	handle := svc.NewService(options.Config, options.Logger)
@@ -88,6 +89,11 @@ func Server(opts ...Option) (http.Service, error) {
 		settingssvc.RegisterPermissionServiceWeb(r, handle)
 	})
 
+	_ = chi.Walk(mux, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	micro.RegisterHandler(service.Server(), mux)
 
 	return service, nil
diff --git a/services/settings/pkg/store/defaults/defaults.go b/services/settings/pkg/store/defaults/defaults.go
index 906ce680024..52cc0b66963 100644
--- a/services/settings/pkg/store/defaults/defaults.go
+++ b/services/settings/pkg/store/defaults/defaults.go
@@ -58,6 +58,16 @@ const (
 	// DeleteAllSpacesPermissionName is the hardcoded setting name for the delete all space permission
 	DeleteAllSpacesPermissionName string = "delete-all-spaces"
 
+	// ManageSpacePropertiesPermissionID is the hardcoded setting UUID for the manage space properties permission
+	ManageSpacePropertiesPermissionID string = "b44b4054-31a2-42b8-bb71-968b15cfbd4f"
+	// ManageSpacePropertiesPermissionName is the hardcoded setting name for the manage space properties permission
+	ManageSpacePropertiesPermissionName string = "Drive.ReadWrite"
+
+	// SpaceAbilityPermissionID is the hardcoded setting UUID for the space ability permission
+	SpaceAbilityPermissionID string = "cf3faa8c-50d9-4f84-9650-ff9faf21aa9d"
+	// SpaceAbilityPermissionName is the hardcoded setting name for the space ability permission
+	SpaceAbilityPermissionName string = "Drive.ReadWriteEnabled"
+
 	settingUUIDProfileLanguage = "aa8cfbe5-95d4-4f7e-a032-c3c01f5f062f"
 
 	// AccountManagementPermissionID is the hardcoded setting UUID for the account management permission
@@ -265,6 +275,36 @@ func generateBundleSpaceAdminRole() *settingsmsg.Bundle {
 			Type: settingsmsg.Resource_TYPE_SYSTEM,
 		},
 		Settings: []*settingsmsg.Setting{
+			{
+				Id:          ManageSpacePropertiesPermissionID,
+				Name:        ManageSpacePropertiesPermissionName,
+				DisplayName: "Manage space properties",
+				Description: "This permission allows to manage space properties such as name and description.",
+				Resource: &settingsmsg.Resource{
+					Type: settingsmsg.Resource_TYPE_SYSTEM,
+				},
+				Value: &settingsmsg.Setting_PermissionValue{
+					PermissionValue: &settingsmsg.Permission{
+						Operation:  settingsmsg.Permission_OPERATION_READWRITE,
+						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+					},
+				},
+			},
+			{
+				Id:          SpaceAbilityPermissionID,
+				Name:        SpaceAbilityPermissionName,
+				DisplayName: "Space ability",
+				Description: "This permission allows to enable and disable spaces.",
+				Resource: &settingsmsg.Resource{
+					Type: settingsmsg.Resource_TYPE_SYSTEM,
+				},
+				Value: &settingsmsg.Setting_PermissionValue{
+					PermissionValue: &settingsmsg.Permission{
+						Operation:  settingsmsg.Permission_OPERATION_READWRITE,
+						Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
+					},
+				},
+			},
 			{
 				Id:          SetSpaceQuotaPermissionID,
 				Name:        SetSpaceQuotaPermissionName,
diff --git a/services/thumbnails/pkg/service/http/v0/service.go b/services/thumbnails/pkg/service/http/v0/service.go
index 428986a3a19..1fe63be6e85 100644
--- a/services/thumbnails/pkg/service/http/v0/service.go
+++ b/services/thumbnails/pkg/service/http/v0/service.go
@@ -55,6 +55,11 @@ func NewService(opts ...Option) Service {
 		r.Get("/data", svc.GetThumbnail)
 	})
 
+	_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	return svc
 }
 
diff --git a/services/web/pkg/service/v0/service.go b/services/web/pkg/service/v0/service.go
index a00e28bcdd9..aeb1c3083df 100644
--- a/services/web/pkg/service/v0/service.go
+++ b/services/web/pkg/service/v0/service.go
@@ -45,6 +45,11 @@ func NewService(opts ...Option) Service {
 		r.Mount("/", svc.Static(options.Config.HTTP.CacheTTL))
 	})
 
+	_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	return svc
 }
 
diff --git a/services/webdav/pkg/service/v0/service.go b/services/webdav/pkg/service/v0/service.go
index 2557b619c72..963abbd6638 100644
--- a/services/webdav/pkg/service/v0/service.go
+++ b/services/webdav/pkg/service/v0/service.go
@@ -134,6 +134,11 @@ func NewService(opts ...Option) (Service, error) {
 		}))
 	})
 
+	_ = chi.Walk(m, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
+		options.Logger.Debug().Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint")
+		return nil
+	})
+
 	return svc, nil
 }
 


Test patch:
diff --git a/docs/ocis/development/testing.md b/docs/ocis/development/testing.md
index b3fa042e9c9..fafe6365e34 100644
--- a/docs/ocis/development/testing.md
+++ b/docs/ocis/development/testing.md
@@ -89,15 +89,12 @@ make -C tests/acceptance/docker clean
 
 ## Testing with test suite natively installed
 
-We are using the ownCloud 10 acceptance test suite against oCIS.
+We have two sets of tests:
+- `test-acceptance-from-core-api` set was transferred from [core](https://github.com/owncloud/core) repository 
+The suite name of all tests transferred from the core starts with "core"
 
-### Getting the tests
+- `test-acceptance-api` set was created for ocis. Mainly for testing spaces features
 
-All you need to do to get the acceptance tests is check out the core repo:
-
-```bash
-git clone https://github.com/owncloud/core.git
-```
 
 ### Run ocis
 
@@ -106,47 +103,54 @@ Create an up-to-date ocis binary by [building oCIS]({{< ref "build" >}})
 To start ocis:
 
 ```bash
-IDM_ADMIN_PASSWORD=admin ocis/bin/ocis init --insecure true
-OCIS_INSECURE=true PROXY_ENABLE_BASIC_AUTH=true ocis/bin/ocis server
+IDM_ADMIN_PASSWORD=admin \
+ocis/bin/ocis init --insecure true
+
+OCIS_INSECURE=true PROXY_ENABLE_BASIC_AUTH=true \
+ocis/bin/ocis server
 ```
 
 `PROXY_ENABLE_BASIC_AUTH` will allow the acceptance tests to make requests against the provisioning api (and other endpoints) using basic auth.
 
-### Run the acceptance tests
-
-First we will need to clone the testing app in owncloud which contains the skeleton files required for running the tests.
-In the ownCloud 10 core clone the testing app with the following command:
+### Run the test-acceptance-from-core-api tests
 
 ```bash
-git clone https://github.com/owncloud/testing apps/testing
+make test-acceptance-from-core-api \
+TEST_SERVER_URL=https://localhost:9200 \
+TEST_WITH_GRAPH_API=true \
+TEST_OCIS=true \
 ```
+Note: This command only works for suites that start with core
 
-Then run the api acceptance tests with the following command from the root of the ownCloud 10 core repository:
+### Run the test-acceptance-api tests
 
 ```bash
 make test-acceptance-api \
 TEST_SERVER_URL=https://localhost:9200 \
 TEST_WITH_GRAPH_API=true \
-PATH_TO_OCIS=/var/www/ocis \
-PATH_TO_CORE=. \
 TEST_OCIS=true \
-STORAGE_DRIVER=OCIS \
-SKELETON_DIR=apps/testing/data/apiSkeleton \
-BEHAT_FILTER_TAGS='~@notToImplementOnOCIS&&~@toImplementOnOCIS'
 ```
 
-Make sure to adjust the settings `TEST_SERVER_URL` and `PATH_TO_OCIS` according to your environment.
+Make sure to adjust the settings `TEST_SERVER_URL` according to your environment.
+
+To run a single feature add `BEHAT_FEATURE=<feature file>` 
+
+example: `BEHAT_SUITE=tests/acceptance/features/apiGraph/createUser.feature`
 
-This will run all tests that are relevant to oCIS.
+To run a single test add `BEHAT_FEATURE=<file.feature:(line number)>`
 
-To run a single feature add `BEHAT_FEATURE=<feature file>`
+example: `BEHAT_SUITE=tests/acceptance/features/apiGraph/createUser.feature:12`
+
+To run a single suite add `BEHAT_SUITE=<test suite>`
+
+example: `BEHAT_SUITE=apiGraph`
 
 To run tests with a different storage driver set `STORAGE_DRIVER` to the correct value. It can be set to `OCIS` or `OWNCLOUD` and uses `OWNCLOUD` as the default value.
 
 ### use existing tests for BDD
 
-As a lot of scenarios are written for oC10, we can use those tests for Behaviour driven development in ocis.
-Every scenario that does not work in oCIS with "ocis" storage, is listed in `tests/acceptance/expected-failures-on-OCIS-storage.md` with a link to the related issue.
+As a lot of scenarios from `test-acceptance-from-core-api` are written for oC10, we can use those tests for Behaviour driven development in ocis.
+Every scenario that does not work in oCIS with "ocis" storage, is listed in `tests/acceptance/expected-failures-API-on-OCIS-storage.md` with a link to the related issue.
 
 Those scenarios are run in the ordinary acceptance test pipeline in CI. The scenarios that fail are checked against the
 expected failures. If there are any differences then the CI pipeline fails.
@@ -155,30 +159,24 @@ The tests are not currently run in CI with the OWNCLOUD or EOS storage drivers,
 
 If you want to work on a specific issue
 
-1. adjust the core commit id to the latest commit in core so that CI will run the latest test code and scenarios from core.
-    For that change `CORE_COMMITID` in `.drone.env`:
-
-        # The test runner source for API tests
-        CORE_COMMITID=38c91e5cf5fc4ffdc0536ba1d147a2a618ef83b5
-        CORE_BRANCH=master
-
-2. locally run each of the tests marked with that issue in the expected failures file.
+1. locally run each of the tests marked with that issue in the expected failures file.
 
     E.g.:
 
     ```bash
-    make test-acceptance-api \
+    make test-acceptance-from-core-api \
     TEST_SERVER_URL=https://localhost:9200 \
     TEST_OCIS=true \
+    TEST_WITH_GRAPH_API=true \
     STORAGE_DRIVER=OCIS \
-    BEHAT_FEATURE='tests/acceptance/features/apiComments/comments.feature:123'
+    BEHAT_FEATURE='tests/acceptance/features/coreApiVersions/fileVersions.feature:147'
     ```
 
-3. the tests will fail, try to understand how and why they are failing
-4. fix the code
-5. go back to 2. and repeat till the tests are passing.
-6. remove those tests from the expected failures file
-7. make a PR that has the fixed code, and the relevant lines removed from the expected failures file.
+2. the tests will fail, try to understand how and why they are failing
+3. fix the code
+4. go back to 1. and repeat till the tests are passing.
+5. remove those tests from the expected failures file
+6. make a PR that has the fixed code, and the relevant lines removed from the expected failures file.
 
 ## Running tests for parallel deployment
 
diff --git a/services/graph/pkg/identity/ldap_education_class_test.go b/services/graph/pkg/identity/ldap_education_class_test.go
index f7271302153..7b5e215dc88 100644
--- a/services/graph/pkg/identity/ldap_education_class_test.go
+++ b/services/graph/pkg/identity/ldap_education_class_test.go
@@ -20,6 +20,7 @@ var classEntry = ldap.NewEntry("ocEducationExternalId=Math0123",
 		"ocEducationClassType":  {"course"},
 		"entryUUID":             {"abcd-defg"},
 	})
+
 var classEntryWithMember = ldap.NewEntry("ocEducationExternalId=Math0123",
 	map[string][]string{
 		"cn":                    {"Math"},
@@ -300,3 +301,245 @@ func TestGetEducationClassMembers(t *testing.T) {
 		}
 	}
 }
+
+func TestLDAP_UpdateEducationClass(t *testing.T) {
+	externalIDs := []string{"Math3210"}
+	changeString := "xxxx-xxxx"
+	type args struct {
+		id    string
+		class libregraph.EducationClass
+	}
+	type modifyData struct {
+		arg *ldap.ModifyRequest
+		ret error
+	}
+	type modifyDNData struct {
+		arg *ldap.ModifyDNRequest
+		ret error
+	}
+	type searchData struct {
+		res *ldap.SearchResult
+		err error
+	}
+	tests := []struct {
+		name         string
+		args         args
+		modifyDNData modifyDNData
+		modifyData   modifyData
+		searchData   searchData
+		assertion    func(assert.TestingT, error, ...interface{}) bool
+	}{
+		{
+			name: "Change name",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					DisplayName: "Math-2",
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Nil(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{
+					DN: "ocEducationExternalId=Math0123",
+					Changes: []ldap.Change{
+						{
+							Operation: ldap.ReplaceAttribute,
+							Modification: ldap.PartialAttribute{
+								Type: "cn",
+								Vals: []string{"Math-2"},
+							},
+						},
+					},
+				},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Change external ID",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					ExternalId: &externalIDs[0],
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Nil(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{
+					DN:           "ocEducationExternalId=Math0123",
+					NewRDN:       "ocEducationExternalId=Math3210",
+					DeleteOldRDN: true,
+					NewSuperior:  "",
+				},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Change both name and external ID",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					DisplayName: "Math-2",
+					ExternalId:  &externalIDs[0],
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Nil(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{
+					DN: "ocEducationExternalId=Math3210,ou=groups,dc=test",
+					Changes: []ldap.Change{
+						{
+							Operation: ldap.ReplaceAttribute,
+							Modification: ldap.PartialAttribute{
+								Type: "cn",
+								Vals: []string{"Math-2"},
+							},
+						},
+					},
+				},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{
+					DN:           "ocEducationExternalId=Math0123",
+					NewRDN:       "ocEducationExternalId=Math3210",
+					DeleteOldRDN: true,
+					NewSuperior:  "",
+				},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Check error: attempt at changing ID",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					Id: &changeString,
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Error(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Check error: attempt at changing description",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					Description: &changeString,
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Error(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Check error: attempt at changing classification",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					Classification: changeString,
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Error(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+		{
+			name: "Check error: attempt at changing members",
+			args: args{
+				id: "abcd-defg",
+				class: libregraph.EducationClass{
+					Members: []libregraph.User{*libregraph.NewUser()},
+				},
+			},
+			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Error(tt, err) },
+			modifyData: modifyData{
+				arg: &ldap.ModifyRequest{},
+			},
+			modifyDNData: modifyDNData{
+				arg: &ldap.ModifyDNRequest{},
+				ret: nil,
+			},
+			searchData: searchData{
+				res: &ldap.SearchResult{
+					Entries: []*ldap.Entry{classEntry},
+				},
+				err: nil,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			lm := &mocks.Client{}
+			b, err := getMockedBackend(lm, eduConfig, &logger)
+			if err != nil {
+				panic(err)
+			}
+
+			lm.On("Modify", tt.modifyData.arg).Return(tt.modifyData.ret)
+			lm.On("ModifyDN", tt.modifyDNData.arg).Return(tt.modifyDNData.ret)
+			lm.On("Search", mock.Anything).Return(tt.searchData.res, tt.searchData.err)
+
+			ctx := context.Background()
+
+			_, err = b.UpdateEducationClass(ctx, tt.args.id, tt.args.class)
+			tt.assertion(t, err)
+		})
+	}
+}
diff --git a/services/graph/pkg/identity/ldap_education_school_test.go b/services/graph/pkg/identity/ldap_education_school_test.go
index 12c25894e4d..49cc1c3dd8b 100644
--- a/services/graph/pkg/identity/ldap_education_school_test.go
+++ b/services/graph/pkg/identity/ldap_education_school_test.go
@@ -303,6 +303,14 @@ var schoolByIDSearch1 *ldap.SearchRequest = &ldap.SearchRequest{
 	Attributes: []string{"ou", "owncloudUUID", "ocEducationSchoolNumber"},
 	Controls:   []ldap.Control(nil),
 }
+var schoolByNumberSearch *ldap.SearchRequest = &ldap.SearchRequest{
+	BaseDN:     "",
+	Scope:      2,
+	SizeLimit:  1,
+	Filter:     filterSchoolSearchByNumberExisting,
+	Attributes: []string{"ou", "owncloudUUID", "ocEducationSchoolNumber"},
+	Controls:   []ldap.Control(nil),
+}
 var userByIDSearch1 *ldap.SearchRequest = &ldap.SearchRequest{
 	BaseDN:     "ou=people,dc=test",
 	Scope:      2,
@@ -320,12 +328,39 @@ var userByIDSearch2 *ldap.SearchRequest = &ldap.SearchRequest{
 	Controls:   []ldap.Control(nil),
 }
 
+var userToSchoolModRequest *ldap.ModifyRequest = &ldap.ModifyRequest{
+	DN: "uid=user,ou=people,dc=test",
+	Changes: []ldap.Change{
+		{
+			Operation: ldap.AddAttribute,
+			Modification: ldap.PartialAttribute{
+				Type: "ocMemberOfSchool",
+				Vals: []string{"abcd-defg"},
+			},
+		},
+	},
+}
+
+var userFromSchoolModRequest *ldap.ModifyRequest = &ldap.ModifyRequest{
+	DN: "uid=user,ou=people,dc=test",
+	Changes: []ldap.Change{
+		{
+			Operation: ldap.DeleteAttribute,
+			Modification: ldap.PartialAttribute{
+				Type: "ocMemberOfSchool",
+				Vals: []string{"abcd-defg"},
+			},
+		},
+	},
+}
+
 func TestAddUsersToEducationSchool(t *testing.T) {
 	lm := &mocks.Client{}
-	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry, schoolEntry1}}, nil)
+	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
+	lm.On("Search", schoolByNumberSearch).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
 	lm.On("Search", userByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil)
 	lm.On("Search", userByIDSearch2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{}}, nil)
-	lm.On("Modify", mock.Anything).Return(nil)
+	lm.On("Modify", userToSchoolModRequest).Return(nil)
 	b, err := getMockedBackend(lm, eduConfig, &logger)
 	assert.Nil(t, err)
 	err = b.AddUsersToEducationSchool(context.Background(), "abcd-defg", []string{"does-not-exist"})
@@ -337,14 +372,19 @@ func TestAddUsersToEducationSchool(t *testing.T) {
 	err = b.AddUsersToEducationSchool(context.Background(), "abcd-defg", []string{"abcd-defg"})
 	lm.AssertNumberOfCalls(t, "Search", 7)
 	assert.Nil(t, err)
+	// try to add by school number (instead or id)
+	err = b.AddUsersToEducationSchool(context.Background(), "0123", []string{"abcd-defg"})
+	lm.AssertNumberOfCalls(t, "Search", 9)
+	assert.Nil(t, err)
 }
 
 func TestRemoveMemberFromEducationSchool(t *testing.T) {
 	lm := &mocks.Client{}
-	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry, schoolEntry1}}, nil)
+	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
+	lm.On("Search", schoolByNumberSearch).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
 	lm.On("Search", userByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntryWithSchool}}, nil)
 	lm.On("Search", userByIDSearch2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{}}, nil)
-	lm.On("Modify", mock.Anything).Return(nil)
+	lm.On("Modify", userFromSchoolModRequest).Return(nil)
 	b, err := getMockedBackend(lm, eduConfig, &logger)
 	assert.Nil(t, err)
 	err = b.RemoveUserFromEducationSchool(context.Background(), "abcd-defg", "does-not-exist")
@@ -354,6 +394,10 @@ func TestRemoveMemberFromEducationSchool(t *testing.T) {
 	err = b.RemoveUserFromEducationSchool(context.Background(), "abcd-defg", "abcd-defg")
 	lm.AssertNumberOfCalls(t, "Search", 4)
 	lm.AssertNumberOfCalls(t, "Modify", 1)
+	// try to remove by school number (instead or id)
+	err = b.RemoveUserFromEducationSchool(context.Background(), "0123", "abcd-defg")
+	lm.AssertNumberOfCalls(t, "Search", 6)
+	lm.AssertNumberOfCalls(t, "Modify", 2)
 	assert.Nil(t, err)
 }
 
@@ -368,10 +412,14 @@ var usersBySchoolIDSearch *ldap.SearchRequest = &ldap.SearchRequest{
 
 func TestGetEducationSchoolUsers(t *testing.T) {
 	lm := &mocks.Client{}
-	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry, schoolEntry1}}, nil)
+	lm.On("Search", schoolByIDSearch1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
+	lm.On("Search", schoolByNumberSearch).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry}}, nil)
 	lm.On("Search", usersBySchoolIDSearch).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntryWithSchool}}, nil)
 	b, _ := getMockedBackend(lm, eduConfig, &logger)
 	users, err := b.GetEducationSchoolUsers(context.Background(), "abcd-defg")
 	assert.Nil(t, err)
 	assert.Equal(t, 1, len(users))
+	users, err = b.GetEducationSchoolUsers(context.Background(), "0123")
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(users))
 }
diff --git a/services/graph/pkg/service/v0/educationclasses_test.go b/services/graph/pkg/service/v0/educationclasses_test.go
index d1f2a9d0b39..ba90311bd25 100644
--- a/services/graph/pkg/service/v0/educationclasses_test.go
+++ b/services/graph/pkg/service/v0/educationclasses_test.go
@@ -284,13 +284,16 @@ var _ = Describe("EducationClass", func() {
 		Context("with an existing group", func() {
 			BeforeEach(func() {
 				identityEducationBackend.On("GetEducationClass", mock.Anything, mock.Anything, mock.Anything).Return(newClass, nil)
+				identityEducationBackend.On("UpdateEducationClass", mock.Anything, mock.Anything, mock.Anything).Return(newClass, nil)
 			})
 
 			It("fails when the number of users is exceeded - spec says 20 max", func() {
 				updatedClass := libregraph.NewEducationClassWithDefaults()
 				updatedClass.SetDisplayName("class updated")
-				updatedClass.SetMembersodataBind([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18",
-					"19", "20", "21"})
+				updatedClass.SetMembersodataBind([]string{
+					"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18",
+					"19", "20", "21",
+				})
 				updatedClassJson, err := json.Marshal(updatedClass)
 				Expect(err).ToNot(HaveOccurred())
 
@@ -323,6 +326,7 @@ var _ = Describe("EducationClass", func() {
 					service.WithGatewayClient(gatewayClient),
 					service.EventsPublisher(&eventsPublisher),
 					service.WithIdentityBackend(identityBackend),
+					service.WithIdentityEducationBackend(identityEducationBackend),
 				)
 
 				r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/classes", bytes.NewBuffer(updatedClassJson))
@@ -408,7 +412,7 @@ var _ = Describe("EducationClass", func() {
 
 			Expect(rr.Code).To(Equal(http.StatusNoContent))
 			identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "DeleteEducationClass", 1)
-			//eventsPublisher.AssertNumberOfCalls(GinkgoT(), "Publish", 1)
+			// eventsPublisher.AssertNumberOfCalls(GinkgoT(), "Publish", 1)
 		})
 	})
 
diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go
index d516782534b..9f21aa1a197 100644
--- a/services/graph/pkg/service/v0/users_test.go
+++ b/services/graph/pkg/service/v0/users_test.go
@@ -372,6 +372,7 @@ var _ = Describe("Users", func() {
 					},
 					{
 						Id:        &provider.StorageSpaceId{OpaqueId: "personal"},
+						Owner:     &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "user1"}},
 						Root:      &provider.ResourceId{SpaceId: "personal", OpaqueId: "personal"},
 						SpaceType: "personal",
 					},
@@ -512,9 +513,6 @@ var _ = Describe("Users", func() {
 		})
 
 		It("handles bad Mails", func() {
-			user.Mail = nil
-			assertHandleBadAttributes(user)
-
 			user.SetMail("not-a-mail-address")
 			assertHandleBadAttributes(user)
 		})
diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php
index 0dccb47832c..80673a4800d 100644
--- a/tests/TestHelpers/GraphHelper.php
+++ b/tests/TestHelpers/GraphHelper.php
@@ -589,7 +589,9 @@ class GraphHelper {
 		$payload['onPremisesSamAccountName'] = $userName;
 		$payload['passwordProfile'] = ['password' => $password];
 		$payload['displayName'] = $displayName ?? $userName;
-		$payload['mail'] = $email ?? $userName . '@example.com';
+		if (!empty($email)) {
+			$payload['mail'] = $email ?? $userName . '@example.com';
+		}
 		return \json_encode($payload);
 	}
 
diff --git a/tests/acceptance/expected-failures-API-on-OCIS-storage.md b/tests/acceptance/expected-failures-API-on-OCIS-storage.md
index a5f191d9552..98181dba5c8 100644
--- a/tests/acceptance/expected-failures-API-on-OCIS-storage.md
+++ b/tests/acceptance/expected-failures-API-on-OCIS-storage.md
@@ -35,15 +35,19 @@ _ocdav: double-check the webdav property parsing when custom namespaces are used
 
 #### [file versions do not report the version author](https://github.com/owncloud/ocis/issues/2914)
 
-- [coreApiVersions/fileVersionAuthor.feature:14](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L14)
-- [coreApiVersions/fileVersionAuthor.feature:37](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L37)
-- [coreApiVersions/fileVersionAuthor.feature:58](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L58)
-- [coreApiVersions/fileVersionAuthor.feature:78](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L78)
-- [coreApiVersions/fileVersionAuthor.feature:104](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L104)
-- [coreApiVersions/fileVersionAuthor.feature:129](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L129)
-- [coreApiVersions/fileVersionAuthor.feature:154](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L154)
-- [coreApiVersions/fileVersionAuthor.feature:180](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L180)
+- [coreApiVersions/fileVersionAuthor.feature:13](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L13)
+- [coreApiVersions/fileVersionAuthor.feature:44](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L44)
+- [coreApiVersions/fileVersionAuthor.feature:71](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L71)
+- [coreApiVersions/fileVersionAuthor.feature:97](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L97)
+- [coreApiVersions/fileVersionAuthor.feature:130](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L130)
+- [coreApiVersions/fileVersionAuthor.feature:157](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L157)
+- [coreApiVersions/fileVersionAuthor.feature:188](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L188)
 - [coreApiVersions/fileVersionAuthor.feature:223](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L223)
+- [coreApiVersions/fileVersionAuthor.feature:275](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L275)
+- [coreApiVersions/fileVersionAuthor.feature:324](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L324)
+- [coreApiVersions/fileVersionAuthor.feature:345](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L345)
+- [coreApiVersions/fileVersionAuthor.feature:365](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L365)
+- [coreApiVersions/fileVersionAuthor.feature:390](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature#L390)
 
 ### Sync
 
@@ -307,8 +311,6 @@ cannot share a folder with create permission
 - [coreApiShareOperationsToShares1/changingFilesShare.feature:131](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareOperationsToShares1/changingFilesShare.feature#L131)
 - [coreApiShareOperationsToShares1/changingFilesShare.feature:132](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareOperationsToShares1/changingFilesShare.feature#L132)
 - [coreApiShareManagementBasicToShares/createShareToSharesFolder.feature:538](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiShareManagementBasicToShares/createShareToSharesFolder.feature#L538)
-- [coreApiVersions/fileVersionsSharingToShares.feature:220](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature#L220)
-- [coreApiVersions/fileVersionsSharingToShares.feature:221](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature#L221)
 - [coreApiWebdavMove2/moveShareOnOcis.feature:30](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOcis.feature#L30)
 - [coreApiWebdavMove2/moveShareOnOcis.feature:32](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOcis.feature#L32)
 - [coreApiWebdavMove2/moveShareOnOcis.feature:98](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOcis.feature#L98)
@@ -465,7 +467,7 @@ cannot share a folder with create permission
 
 #### [user can access version metadata of a received share before accepting it](https://github.com/owncloud/ocis/issues/760)
 
-- [coreApiVersions/fileVersionsSharingToShares.feature:283](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature#L283)
+- [coreApiVersions/fileVersions.feature:487](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/coreApiVersions/fileVersions.feature#L487)
 
 #### [Share lists deleted user as 'user'](https://github.com/owncloud/ocis/issues/903)
 
diff --git a/tests/acceptance/features/apiGraph/createUser.feature b/tests/acceptance/features/apiGraph/createUser.feature
index 1378a4be7de..cd62336c0fb 100644
--- a/tests/acceptance/features/apiGraph/createUser.feature
+++ b/tests/acceptance/features/apiGraph/createUser.feature
@@ -24,7 +24,7 @@ Feature: create user
       | withoutPassSameEmail         | without pass     | alice@example.org   |                              | 200  | should     |
       | name                         | pass with space  | example@example.org | my pass                      | 200  | should     |
       | nameWithCharacters(*:!;_+-&) | user             | new@example.org     | 123                          | 400  | should not |
-      | withoutEmail                 | without email    |                     | 123                          | 400  | should not |
+      | withoutEmail                 | without email    |                     | 123                          | 200  | should     |
       | Alice                        | same userName    | new@example.org     | 123                          | 400  | should     |
       | name with space              | name with space  | example@example.org | 123                          | 400  | should not |
 
diff --git a/tests/acceptance/features/apiSpaces/tag.feature b/tests/acceptance/features/apiSpaces/tag.feature
index a628935f122..c452072a819 100644
--- a/tests/acceptance/features/apiSpaces/tag.feature
+++ b/tests/acceptance/features/apiSpaces/tag.feature
@@ -78,3 +78,166 @@ Feature: Tag
       | important                     |
       | fileTag                       |
       | tag with symbol @^$#^%$@%!_+) |
+
+
+  Scenario Outline: Member of the space tries to create tag
+    Given user "Alice" has shared a space "use-tag" to user "Brian" with role "<role>"
+    When user "Brian" creates the following tags for folder "folderMain/insideTheFolder.txt" of space "use-tag":
+      | tag level#1                    |
+      | tag with symbols @^$#^%$@%!_+) |
+    Then the HTTP status code should be "<code>"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response <shouldOrNot> contain following tags:
+      | tag level#1                    |
+      | tag with symbols @^$#^%$@%!_+) |
+    Examples:
+      | role    | code | shouldOrNot |
+      | viewer  | 403  | should not  |
+      | editor  | 200  | should      |
+      | manager | 200  | should      |
+
+
+  Scenario: The recipient has a created tags if share is accepted
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag |
+      | marketing |
+    And user "Alice" has shared the following entity "folderMain" inside of space "use-tag" with user "Brian" with role "viewer"
+    When user "Brian" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should not contain following tags:
+      | folderTag |
+      | marketing |
+    When user "Brian" accepts share "/folderMain" offered by user "Alice" using the sharing API
+    And user "Brian" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should contain following tags:
+      | folderTag |
+      | marketing |
+
+
+  Scenario Outline: The recipient of the shared resource tries to create a tag
+    Given user "Alice" has shared the following entity "folderMain" inside of space "use-tag" with user "Brian" with role "<role>"
+    And user "Brian" has accepted share "/folderMain" offered by user "Alice"
+    When user "Brian" creates the following tags for <resource> "<resourceName>" of space "Shares":
+      | tag in a shared resource |
+      | second tag               |
+    Then the HTTP status code should be "<code>"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response <shouldOrNot> contain following tags:
+      | tag in a shared resource |
+      | second tag               |
+    Examples:
+      | role    | resource | resourceName                   | code | shouldOrNot |
+      | viewer  | file     | folderMain/insideTheFolder.txt | 403  | should not  |
+      | editor  | file     | folderMain/insideTheFolder.txt | 200  | should      |
+      | manager | file     | folderMain/insideTheFolder.txt | 200  | should      |
+      | viewer  | folder   | folderMain                     | 403  | should not  |
+      | editor  | folder   | folderMain                     | 200  | should      |
+      | manager | folder   | folderMain                     | 200  | should      |
+
+
+  Scenario Outline: The recipient of the shared resource tries to remove a tag
+    Given user "Alice" has shared the following entity "folderMain" inside of space "use-tag" with user "Brian" with role "<role>"
+    And user "Alice" has created the following tags for <resource> "<resourceName>" of the space "use-tag":
+      | tag in a shared resource |
+      | second tag               |
+    And user "Brian" has accepted share "/folderMain" offered by user "Alice"
+    When user "Brian" removes the following tags for <resource> "<resourceName>" of space "Shares":
+      | tag in a shared resource |
+      | second tag               |
+    Then the HTTP status code should be "<code>"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response <shouldOrNot> contain following tags:
+      | tag in a shared resource |
+      | second tag               |
+    Examples:
+      | role    | resource | resourceName                   | code | shouldOrNot |
+      | viewer  | file     | folderMain/insideTheFolder.txt | 403  | should      |
+      | editor  | file     | folderMain/insideTheFolder.txt | 200  | should not  |
+      | manager | file     | folderMain/insideTheFolder.txt | 200  | should not  |
+      | viewer  | folder   | folderMain                     | 403  | should      |
+      | editor  | folder   | folderMain                     | 200  | should not  |
+      | manager | folder   | folderMain                     | 200  | should not  |
+
+
+  Scenario: User removes folder tags
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag   |
+      | marketing   |
+      | development |
+    When user "Alice" removes the following tags for folder "folderMain" of space "use-tag":
+      | folderTag |
+      | marketing |
+    And user "Alice" sends PROPFIND request from the space "use-tag" to the resource "folderMain" using the WebDAV API
+    Then the HTTP status code should be "207"
+    And the "PROPFIND" response should contain a space "use-tag" with these key and value pairs:
+      | key     | value       |
+      | oc:tags | development |
+
+
+  Scenario: User lists tags after deleting some folder tags
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag   |
+      | marketing   |
+      | development |
+    When user "Alice" removes the following tags for folder "folderMain" of space "use-tag":
+      | folderTag |
+      | marketing |
+    Then the HTTP status code should be "200"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should contain following tags:
+      | development |
+    And the response should not contain following tags:
+      | folderTag |
+      | marketing |
+
+
+  Scenario: User lists the tags after deleting a folder
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag |
+      | marketing |
+    When user "Alice" removes the folder "folderMain" from space "use-tag"
+    Then the HTTP status code should be "204"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should not contain following tags:
+      | folderTag |
+      | marketing |
+
+
+  Scenario: User lists the tags after deleting a space
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag |
+      | marketing |
+    When user "Alice" disables a space "use-tag"
+    Then the HTTP status code should be "204"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should contain following tags:
+      | folderTag |
+      | marketing |
+    When user "Alice" deletes a space "use-tag"
+    Then the HTTP status code should be "204"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should not contain following tags:
+      | folderTag |
+      | marketing |
+
+
+  Scenario: User lists the tags after restoring a deleted folder
+    Given user "Alice" has created the following tags for folder "folderMain" of the space "use-tag":
+      | folderTag |
+      | marketing |
+    And user "Alice" has removed the folder "folderMain" from space "use-tag"
+    When user "Alice" restores the folder "folderMain" from the trash of the space "use-tag" to "/folderMain"
+    Then the HTTP status code should be "201"
+    When user "Alice" lists all available tags via the GraphApi
+    Then the HTTP status code should be "200"
+    And the response should contain following tags:
+      | folderTag |
+      | marketing |
diff --git a/tests/acceptance/features/bootstrap/TagContext.php b/tests/acceptance/features/bootstrap/TagContext.php
index 60dd67e2bdd..f8610b01771 100644
--- a/tests/acceptance/features/bootstrap/TagContext.php
+++ b/tests/acceptance/features/bootstrap/TagContext.php
@@ -101,7 +101,7 @@ class TagContext implements Context {
 	}
 
 	/**
-	 * @Given /^user "([^"]*)" has created the following tags a (folder|file)\s?"([^"]*)" of the space "([^"]*)":$/
+	 * @Given /^user "([^"]*)" has created the following tags for (folder|file)\s?"([^"]*)" of the space "([^"]*)":$/
 	 *
 	 * @param string $user
 	 * @param string $fileOrFolder   (file|folder)
@@ -126,6 +126,8 @@ class TagContext implements Context {
 	 * @throws Exception
 	 */
 	public function theUserGetsAllAvailableTags(string $user):void {
+		// after creating or deleting tags, in some cases tags do not appear or disappear immediately, so we use waiting
+		sleep(1);
 		$this->featureContext->setResponse(
 			GraphHelper::getTags(
 				$this->featureContext->getBaseUrl(),
@@ -136,18 +138,58 @@ class TagContext implements Context {
 	}
 
 	/**
-	 * @Then the response should contain following tag(s):
+	 * @Then /^the response should (not|)\s?contain following tag(s):$/
 	 *
+	 * @param string    $shouldOrNot   (not|)
 	 * @param TableNode $table
 	 *
 	 * @return void
 	 * @throws Exception
 	 */
-	public function theFollowingTagsShouldExistForUser(TableNode $table):void {
+	public function theFollowingTagsShouldExistForUser(string $shouldOrNot, TableNode $table):void {
 		$rows = $table->getRows();
 		foreach ($rows as $row) {
 			$responseArray = $this->featureContext->getJsonDecodedResponse($this->featureContext->getResponse())['value'];
-			Assert::assertTrue(\in_array($row[0], $responseArray), "the response does not contain the tag $row[0]");
+			if ($shouldOrNot === "not") {
+				Assert::assertFalse(\in_array($row[0], $responseArray), "the response should not contain the tag $row[0]");
+			} else {
+				Assert::assertTrue(\in_array($row[0], $responseArray), "the response does not contain the tag $row[0]");
+			}
 		}
 	}
+
+	/**
+	 * @When /^user "([^"]*)" removes the following tags for (folder|file)\s?"([^"]*)" of space "([^"]*)":$/
+	 *
+	 * @param string $user
+	 * @param string $fileOrFolder   (file|folder)
+	 * @param string $resource
+	 * @param string $space
+	 * @param TableNode $table
+	 *
+	 * @return void
+	 * @throws Exception
+	 */
+	public function userRemovesTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
+		$tagNameArray = [];
+		foreach ($table->getRows() as $value) {
+			array_push($tagNameArray, $value[0]);
+		}
+
+		if ($fileOrFolder === 'folder') {
+			$resourceId = $this->spacesContext->getFolderId($user, $space, $resource);
+		} else {
+			$resourceId = $this->spacesContext->getFileId($user, $space, $resource);
+		}
+
+		$response = GraphHelper::deleteTags(
+			$this->featureContext->getBaseUrl(),
+			$this->featureContext->getStepLineRef(),
+			$user,
+			$this->featureContext->getPasswordForUser($user),
+			$resourceId,
+			$tagNameArray
+		);
+		$this->featureContext->setResponse($response);
+	}
 }
diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php
index d966840cd86..af9a0b1212c 100644
--- a/tests/acceptance/features/bootstrap/WebDav.php
+++ b/tests/acceptance/features/bootstrap/WebDav.php
@@ -5497,13 +5497,23 @@ trait WebDav {
 		}
 
 		// the username should be in oc:meta-version-edited-by
-		$xmlPart = $resXml->xpath("//oc:meta-version-edited-by//text()");
-		if (!isset($xmlPart[$index - 1])) {
+		$xmlPart = $resXml->xpath("//oc:meta-version-edited-by");
+		$authors = [];
+		foreach ($xmlPart as $idx => $author) {
+			// The first element is the root path element which is not a version
+			// So skipping it
+			if ($idx !== 0) {
+				$authors[] = $author->__toString();
+			}
+		}
+		// reverse the array to get the latest version first
+		\array_reverse($authors);
+		if (!isset($authors[$index - 1])) {
 			Assert::fail(
 				'could not find version with index "' . $index . '" for oc:meta-version-edited-by property in response to user "' . $this->responseUser . '"'
 			);
 		}
-		$actualUser = $xmlPart[$index - 1][0];
+		$actualUser = $authors[$index - 1];
 		Assert::assertEquals(
 			$expectedUsername,
 			$actualUser,
@@ -5511,17 +5521,27 @@ trait WebDav {
 		);
 
 		// the user's display name should be in oc:meta-version-edited-by-name
-		$xmlPart = $resXml->xpath("//oc:meta-version-edited-by-name//text()");
-		if (!isset($xmlPart[$index - 1])) {
+		$xmlPart = $resXml->xpath("//oc:meta-version-edited-by-name");
+		$displaynames = [];
+		foreach ($xmlPart as $idx => $displayname) {
+			// The first element is the root path element which is not a version
+			// So skipping it
+			if ($idx !== 0) {
+				$displaynames[] = $displayname->__toString();
+			}
+		}
+		// reverse the array to get the latest version first
+		\array_reverse($displaynames);
+		if (!isset($displaynames[$index - 1])) {
 			Assert::fail(
 				'could not find version with index "' . $index . '" for oc:meta-version-edited-by-name property in response to user "' . $this->responseUser . '"'
 			);
 		}
-		$actualUserDisplayName = $xmlPart[$index - 1][0];
+		$actualUserDisplayName = $displaynames[$index - 1];
 		Assert::assertEquals(
 			$expectedUserDisplayName,
 			$actualUserDisplayName,
-			"Expected display name of version with index $index in response to user '$this->responseUser' was '$expectedUsername', but got '$actualUser'"
+			"Expected display name of version with index $index in response to user '$this->responseUser' was '$expectedUserDisplayName', but got '$actualUserDisplayName'"
 		);
 	}
 }
diff --git a/tests/acceptance/features/coreApiSharePublicLink3/updatePublicLinkShare.feature b/tests/acceptance/features/coreApiSharePublicLink3/updatePublicLinkShare.feature
index f1dc0b51f57..848da56477d 100644
--- a/tests/acceptance/features/coreApiSharePublicLink3/updatePublicLinkShare.feature
+++ b/tests/acceptance/features/coreApiSharePublicLink3/updatePublicLinkShare.feature
@@ -610,3 +610,28 @@ Feature: update a public link share
       | dav-path | ocs_api_version | ocs_status_code |
       | spaces   | 1               | 100             |
       | spaces   | 2               | 200             |
+
+
+  Scenario Outline: Updating the role of a public link to internal
+    Given using OCS API version "<ocs_api_version>"
+    And using <dav-path> DAV path
+    And user "Alice" has uploaded file "filesForUpload/textfile.txt" to "/textfile.txt"
+    And user "Alice" has created a public link share with settings
+      | path        | /textfile.txt |
+      | permissions | read          |
+    When user "Alice" updates the last public link share using the sharing API with
+      | permissions | 0 |
+    Then the OCS status code should be "<ocs_status_code>"
+    And the HTTP status code should be "200"
+    Examples:
+      | dav-path | ocs_api_version | ocs_status_code |
+      | old      | 1               | 100             |
+      | old      | 2               | 200             |
+      | new      | 1               | 100             |
+      | new      | 2               | 200             |
+
+    @personalSpace
+    Examples:
+      | dav-path | ocs_api_version | ocs_status_code |
+      | spaces   | 1               | 100             |
+      | spaces   | 2               | 200             |
diff --git a/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature b/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature
index dd04dbbaecc..d04ffcbf743 100644
--- a/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature
+++ b/tests/acceptance/features/coreApiVersions/fileVersionAuthor.feature
@@ -1,5 +1,4 @@
-@api @files_versions-app-required @issue-ocis-reva-275
-
+@api @issue-2914
 Feature: file versions remember the author of each version
 
   Background:
@@ -10,30 +9,38 @@ Feature: file versions remember the author of each version
     And user "Carol" has been created with default attributes and without skeleton files
     And the administrator has enabled the file version storage feature
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users
     Given user "David" has been created with default attributes and without skeleton files
     And user "Alice" has created folder "/test"
     And user "Alice" has shared folder "/test" with user "Brian" with permissions "all"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
     And user "Alice" has shared folder "/test" with user "Carol" with permissions "all"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     And user "Alice" has shared folder "/test" with user "David" with permissions "all"
+    And user "David" has accepted share "/test" offered by user "Alice"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/test/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/test/textfile0.txt"
-    And user "David" has uploaded file with content "uploaded content david" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test/textfile0.txt"
+    And user "David" has uploaded file with content "uploaded content david" to "/Shares/test/textfile0.txt"
     When user "Alice" gets the number of versions of file "/test/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "3"
     And the content of version index "1" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content carol"
     And the content of version index "2" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "3" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Brian,Carol,David" the authors of the versions of file "/test/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Brian  |
+      | 3     | Alice  |
+    And as users "Brian,Carol,David" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
       | 3     | Alice  |
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users for shared folder in the group
     Given user "Alice" has created folder "/test"
     And group "grp1" has been created
@@ -41,20 +48,26 @@ Feature: file versions remember the author of each version
     And user "Brian" has been added to group "grp1"
     And user "Carol" has been added to group "grp1"
     And user "Alice" has shared folder "/test" with group "grp1"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/test/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test/textfile0.txt"
     When user "Alice" gets the number of versions of file "/test/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "2"
     And the content of version index "1" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "2" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Brian,Carol" the authors of the versions of file "/test/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+      | 2     | Alice  |
+    And as users "Brian,Carol" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users for shared file in the group
     Given group "grp1" has been created
     And user "Alice" has been added to group "grp1"
@@ -62,19 +75,25 @@ Feature: file versions remember the author of each version
     And user "Carol" has been added to group "grp1"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/textfile0.txt"
     And user "Alice" has shared file "/textfile0.txt" with group "grp1"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/textfile0.txt"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    And user "Carol" has accepted share "/textfile0.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/textfile0.txt"
     When user "Alice" gets the number of versions of file "textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "2"
     And the content of version index "1" of file "/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "2" of file "/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Brian,Carol" the authors of the versions of file "/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/textfile0.txt" should be:
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
+    And as users "Brian,Carol" the authors of the versions of file "/Shares/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+      | 2     | Alice  |
+
 
-  @skip_on_objectstore @skipOnEncryption @issue-encryption-321
   Scenario: enable file versioning and check the history of changes from multiple users while moving file in/out of a subfolder
     Given user "Alice" has created folder "/test"
     And group "grp1" has been created
@@ -82,25 +101,32 @@ Feature: file versions remember the author of each version
     And user "Brian" has been added to group "grp1"
     And user "Carol" has been added to group "grp1"
     And user "Alice" has shared folder "/test" with group "grp1"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/test/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/test/textfile0.txt"
-    And user "Brian" has moved file "/test/textfile0.txt" to "/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian after moving file outside subfolder" to "/textfile0.txt"
-    And user "Brian" has moved file "/textfile0.txt" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test/textfile0.txt"
+    And user "Alice" has moved file "/test/textfile0.txt" to "/textfile0.txt"
+    And user "Alice" has uploaded file with content "uploaded content alice after moving file outside subfolder" to "/textfile0.txt"
+    And user "Alice" has moved file "/textfile0.txt" to "/test/textfile0.txt"
     When user "Alice" gets the number of versions of file "/test/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "3"
     And the content of version index "1" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content carol"
     And the content of version index "2" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "3" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Brian,Carol" the authors of the versions of file "/test/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
       | 3     | Alice  |
+    And as users "Brian,Carol" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Brian  |
+      | 3     | Alice  |
+
 
-  @skip_on_objectstore
   Scenario: enable file versioning and check the history of changes from multiple users after renaming file by sharer
     Given group "grp1" has been created
     And user "Alice" has been added to group "grp1"
@@ -108,8 +134,10 @@ Feature: file versions remember the author of each version
     And user "Carol" has been added to group "grp1"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/exist.txt"
     And user "Alice" has shared file "/exist.txt" with group "grp1"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/exist.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/exist.txt"
+    And user "Brian" has accepted share "/exist.txt" offered by user "Alice"
+    And user "Carol" has accepted share "/exist.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/exist.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/exist.txt"
     And user "Alice" has moved file "/exist.txt" to "/textfile0.txt"
     When user "Alice" gets the number of versions of file "textfile0.txt"
     Then the HTTP status code should be "207"
@@ -120,12 +148,12 @@ Feature: file versions remember the author of each version
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
-    And as users "Brian,Carol" the authors of the versions of file "/exist.txt" should be:
+    And as users "Brian,Carol" the authors of the versions of file "/Shares/exist.txt" should be:
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes in sharer after renaming file by sharee
     Given group "grp1" has been created
     And user "Alice" has been added to group "grp1"
@@ -133,24 +161,30 @@ Feature: file versions remember the author of each version
     And user "Carol" has been added to group "grp1"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/exist.txt"
     And user "Alice" has shared file "/exist.txt" with group "grp1"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/exist.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/exist.txt"
-    And user "Brian" has moved file "/exist.txt" to "/textfile0.txt"
+    And user "Brian" has accepted share "/exist.txt" offered by user "Alice"
+    And user "Carol" has accepted share "/exist.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/exist.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/exist.txt"
+    And user "Brian" has moved file "/Shares/exist.txt" to "/Shares/textfile0.txt"
     When user "Alice" gets the number of versions of file "exist.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "2"
     And the content of version index "1" of file "/exist.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "2" of file "/exist.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Carol" the authors of the versions of file "/exist.txt" should be:
+    And as users "Alice" the authors of the versions of file "/exist.txt" should be:
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
-    And as user "Brian" the authors of the versions of file "/textfile0.txt" should be:
+    And as users "Carol" the authors of the versions of file "/Shares/exist.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+      | 2     | Alice  |
+    And as user "Brian" the authors of the versions of file "/Shares/textfile0.txt" should be:
       | index | author |
       | 1     | Brian  |
       | 2     | Alice  |
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users when reshared after unshared by sharer
     Given user "Alice" has created folder "/test"
     And group "grp1" has been created
@@ -158,52 +192,70 @@ Feature: file versions remember the author of each version
     And user "Brian" has been added to group "grp1"
     And user "Carol" has been added to group "grp1"
     And user "Alice" has shared folder "/test" with group "grp1"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/test/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test/textfile0.txt"
     And user "Alice" has deleted the last share
     And user "Alice" has uploaded file with content "uploaded content alice after unshared folder" to "/test/textfile0.txt"
     And user "Alice" has shared folder "/test" with group "grp1"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     When user "Alice" gets the number of versions of file "/test/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "3"
     And the content of version index "1" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content carol"
     And the content of version index "2" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "3" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,Brian,Carol" the authors of the versions of file "/test/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Brian  |
+      | 3     | Alice  |
+    And as users "Brian,Carol" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
       | 3     | Alice  |
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users who have a matching folder/file
     Given user "David" has been created with default attributes and without skeleton files
     And user "Brian" has created folder "/test"
     And user "Brian" has uploaded file with content "duplicate brian" to "/test/textfile0.txt"
     And user "Brian" has uploaded file with content "overwrite brian" to "/test/textfile0.txt"
+    And user "Brian" has shared folder "/test" with user "Carol" with permissions "all"
+    And user "Carol" has accepted share "/test" offered by user "Brian"
+    And user "Brian" has shared folder "/test" with user "David" with permissions "all"
+    And user "David" has accepted share "/test" offered by user "Brian"
     And user "Carol" has created folder "/test"
     And user "Carol" has uploaded file with content "duplicate carol" to "/test/textfile0.txt"
+    And user "Carol" has shared folder "/test" with user "Brian" with permissions "all"
+    And user "Brian" has accepted share "/test" offered by user "Carol"
     And user "Alice" has created folder "/test"
     And user "Alice" has shared folder "/test" with user "Brian" with permissions "all"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
     And user "Alice" has shared folder "/test" with user "Carol" with permissions "all"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
     And user "Alice" has shared folder "/test" with user "David" with permissions "all"
+    And user "David" has accepted share "/test" offered by user "Alice"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/test (2)/textfile0.txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/test (2)/textfile0.txt"
-    And user "David" has uploaded file with content "uploaded content david" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test (1)/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test (1)/textfile0.txt"
+    And user "David" has uploaded file with content "uploaded content david" to "/Shares/test (1)/textfile0.txt"
     When user "Alice" gets the number of versions of file "/test/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "3"
     And the content of version index "1" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content carol"
     And the content of version index "2" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "3" of file "/test/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,David" the authors of the versions of file "/test/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
       | 3     | Alice  |
-    And as users "Brian,Carol" the authors of the versions of file "/test (2)/textfile0.txt" should be:
+    And as users "Brian,Carol,David" the authors of the versions of file "/Shares/test (1)/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
@@ -219,31 +271,40 @@ Feature: file versions remember the author of each version
     Then the HTTP status code should be "207"
     And the number of versions should be "0"
 
-  @skip_on_objectstore
+
   Scenario: enable file versioning and check the history of changes from multiple users who have a matching file
     Given user "David" has been created with default attributes and without skeleton files
     And user "Brian" has uploaded file with content "duplicate brian" to "/textfile0.txt"
     And user "Brian" has uploaded file with content "overwrite brian" to "/textfile0.txt"
+    And user "Brian" has shared file "/textfile0.txt" with user "Carol"
+    And user "Carol" has accepted share "/textfile0.txt" offered by user "Brian"
+    And user "Brian" has shared file "/textfile0.txt" with user "David"
+    And user "David" has accepted share "/textfile0.txt" offered by user "Brian"
     And user "Carol" has uploaded file with content "duplicate carol" to "/textfile0.txt"
+    And user "Carol" has shared file "/textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Carol"
     And user "Alice" has uploaded file with content "uploaded content alice" to "/textfile0.txt"
     And user "Alice" has shared file "/textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
     And user "Alice" has shared file "/textfile0.txt" with user "Carol"
+    And user "Carol" has accepted share "/textfile0.txt" offered by user "Alice"
     And user "Alice" has shared file "/textfile0.txt" with user "David"
-    And user "Brian" has uploaded file with content "uploaded content brian" to "/textfile0 (2).txt"
-    And user "Carol" has uploaded file with content "uploaded content carol" to "/textfile0 (2).txt"
-    And user "David" has uploaded file with content "uploaded content david" to "/textfile0.txt"
+    And user "David" has accepted share "/textfile0.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/textfile0 (1).txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/textfile0 (1).txt"
+    And user "David" has uploaded file with content "uploaded content david" to "/Shares/textfile0 (1).txt"
     When user "Alice" gets the number of versions of file "/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "3"
     And the content of version index "1" of file "/textfile0.txt" for user "Alice" should be "uploaded content carol"
     And the content of version index "2" of file "/textfile0.txt" for user "Alice" should be "uploaded content brian"
     And the content of version index "3" of file "/textfile0.txt" for user "Alice" should be "uploaded content alice"
-    And as users "Alice,David" the authors of the versions of file "/textfile0.txt" should be:
+    And as users "Alice" the authors of the versions of file "/textfile0.txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
       | 3     | Alice  |
-    And as users "Brian,Carol" the authors of the versions of file "/textfile0 (2).txt" should be:
+    And as users "Brian,Carol,David" the authors of the versions of file "/Shares/textfile0 (1).txt" should be:
       | index | author |
       | 1     | Carol  |
       | 2     | Brian  |
@@ -258,3 +319,95 @@ Feature: file versions remember the author of each version
     When user "Carol" gets the number of versions of file "/textfile0.txt"
     Then the HTTP status code should be "207"
     And the number of versions should be "0"
+
+
+  Scenario: enable file versioning and check the version author after restoring a version of a file inside a folder
+    Given user "Alice" has created folder "/test"
+    And user "Alice" has shared folder "/test" with user "Brian" with permissions "all"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And user "Alice" has shared folder "/test" with user "Carol" with permissions "all"
+    And user "Carol" has accepted share "/test" offered by user "Alice"
+    And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/test/textfile0.txt"
+    When user "Brian" restores version index "1" of file "/Shares/test/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Alice  |
+    And as user "Brian,Carol" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Alice  |
+
+
+  Scenario: enable file versioning and check the version author after restoring a version of a file
+    Given user "Alice" has uploaded file with content "uploaded content alice" to "/textfile0.txt"
+    And user "Alice" has shared file "/textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    And user "Alice" has shared file "/textfile0.txt" with user "Carol"
+    And user "Carol" has accepted share "/textfile0.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/textfile0.txt"
+    And user "Carol" has uploaded file with content "uploaded content carol" to "/Shares/textfile0.txt"
+    When user "Brian" restores version index "1" of file "/Shares/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Alice  |
+    And as user "Brian,Carol" the authors of the versions of file "/Shares/textfile0.txt" should be:
+      | index | author |
+      | 1     | Carol  |
+      | 2     | Alice  |
+
+
+  Scenario: check the author of the file version which was created before enabling the version storage
+    Given the administrator has disabled the file version storage feature
+    And user "Alice" has uploaded file with content "uploaded content alice" to "/textfile0.txt"
+    And user "Alice" has shared folder "/textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    And the administrator has enabled the file version storage feature
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/textfile0.txt"
+    When user "Brian" restores version index "1" of file "/Shares/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+    And as user "Brian" the authors of the versions of file "/Shares/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+    When user "Brian" restores version index "1" of file "/Shares/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/textfile0.txt" should be:
+      | index | author |
+      | 1     |        |
+    And as user "Brian" the authors of the versions of file "/Shares/textfile0.txt" should be:
+      | index | author |
+      | 1     |        |
+
+
+  Scenario: check the author of the file version (inside a folder) which was created before enabling the version storage
+    Given user "Alice" has created folder "/test"
+    And the administrator has disabled the file version storage feature
+    And user "Alice" has uploaded file with content "uploaded content alice" to "/test/textfile0.txt"
+    And user "Alice" has shared folder "/test" with user "Brian" with permissions "all"
+    And user "Brian" has accepted share "/test" offered by user "Alice"
+    And the administrator has enabled the file version storage feature
+    And user "Brian" has uploaded file with content "uploaded content brian" to "/Shares/test/textfile0.txt"
+    When user "Brian" restores version index "1" of file "/Shares/test/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+    And as user "Brian" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
+      | index | author |
+      | 1     | Brian  |
+    When user "Brian" restores version index "1" of file "/Shares/test/textfile0.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And as user "Alice" the authors of the versions of file "/test/textfile0.txt" should be:
+      | index | author |
+      | 1     |        |
+    And as user "Brian" the authors of the versions of file "/Shares/test/textfile0.txt" should be:
+      | index | author |
+      | 1     |        |
\ No newline at end of file
diff --git a/tests/acceptance/features/coreApiVersions/fileVersions.feature b/tests/acceptance/features/coreApiVersions/fileVersions.feature
index 38a5ef2a7aa..4d4f84ea667 100644
--- a/tests/acceptance/features/coreApiVersions/fileVersions.feature
+++ b/tests/acceptance/features/coreApiVersions/fileVersions.feature
@@ -21,19 +21,6 @@ Feature: dav-versions
     And the version folder of file "/davtest.txt-newdav-regular" for user "Alice" should contain "0" elements
     And the version folder of file "/davtest.txt-olddav-oldchunking" for user "Alice" should contain "0" elements
 
-  @issue-ocis-reva-17 @issue-ocis-reva-56 @notToImplementOnOCIS @newChunking @issue-ocis-1321
-  Scenario: Upload file and no version is available using new chunking
-    When user "Alice" uploads file "filesForUpload/davtest.txt" to "/davtest.txt" in 2 chunks with new chunking and using the WebDAV API
-    Then the HTTP status code should be "201"
-    And the version folder of file "/davtest.txt" for user "Alice" should contain "0" elements
-
-  @issue-ocis-reva-56 @notToImplementOnOCIS @newChunking @issue-ocis-1321
-  Scenario: Upload file and no version is available using async upload
-    Given the administrator has enabled async operations
-    When user "Alice" uploads file "filesForUpload/davtest.txt" asynchronously to "/davtest.txt" in 3 chunks with new chunking and using the WebDAV API
-    Then the HTTP status code should be "202"
-    And the version folder of file "/davtest.txt" for user "Alice" should contain "0" elements
-
   @smokeTest
   Scenario: Upload a file twice and versions are available
     When user "Alice" uploads file "filesForUpload/davtest.txt" to "/davtest.txt" using the WebDAV API
@@ -51,21 +38,6 @@ Feature: dav-versions
     And the version folder of file "/davtest.txt-newdav-regular" for user "Alice" should contain "1" element
     And the version folder of file "/davtest.txt-olddav-oldchunking" for user "Alice" should contain "1" element
 
-  @issue-ocis-reva-17 @issue-ocis-reva-56 @notToImplementOnOCIS @newChunking @issue-ocis-1321
-  Scenario: Upload a file twice and versions are available using new chunking
-    When user "Alice" uploads file "filesForUpload/davtest.txt" to "/davtest.txt" in 2 chunks with new chunking and using the WebDAV API
-    And user "Alice" uploads file "filesForUpload/davtest.txt" to "/davtest.txt" in 2 chunks with new chunking and using the WebDAV API
-    Then the HTTP status code of responses on each endpoint should be "201, 204" respectively
-    And the version folder of file "/davtest.txt" for user "Alice" should contain "1" element
-
-  @issue-ocis-reva-17 @issue-ocis-reva-56 @notToImplementOnOCIS @newChunking @issue-ocis-1321
-  Scenario: Upload a file twice and versions are available using async upload
-    Given the administrator has enabled async operations
-    When user "Alice" uploads file "filesForUpload/davtest.txt" asynchronously to "/davtest.txt" in 2 chunks with new chunking and using the WebDAV API
-    And user "Alice" uploads file "filesForUpload/davtest.txt" asynchronously to "/davtest.txt" in 3 chunks with new chunking and using the WebDAV API
-    Then the HTTP status code of responses on all endpoints should be "202"
-    And the version folder of file "/davtest.txt" for user "Alice" should contain "1" element
-
   @smokeTest
   Scenario: Remove a file
     Given user "Alice" has uploaded file "filesForUpload/davtest.txt" to "/davtest.txt"
@@ -109,21 +81,6 @@ Feature: dav-versions
     Examples:
       | dav-path | status-code |
       | old      | 201         |
-    @notToImplementOnOCIS @newChunking @issue-ocis-1321
-    Examples:
-      | dav-path | status-code |
-      | new      | 204         |
-
-  @skipOnStorage:ceph @files_primary_s3-issue-161 @notToImplementOnOCIS @newChunking @issue-ocis-1321 @issue-ocis-reva-17 @issue-ocis-reva-56 @skipOnStorage:scality
-  Scenario: Uploading a file asynchronously does create the correct version that can be restored
-    Given the administrator has enabled async operations
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    When user "Alice" uploads file "filesForUpload/davtest.txt" asynchronously to "textfile0.txt" in 2 chunks using the WebDAV API
-    And user "Alice" uploads file "filesForUpload/lorem.txt" asynchronously to "textfile0.txt" in 2 chunks using the WebDAV API
-    And user "Alice" restores version index "1" of file "/textfile0.txt" using the WebDAV API
-    Then the HTTP status code of responses on all endpoints should be "202"
-    And the version folder of file "/textfile0.txt" for user "Alice" should contain "2" elements
-    And the content of file "/textfile0.txt" for user "Alice" should be "Dav-Test"
 
   @skipOnStorage:ceph @skipOnStorage:scality @files_primary_s3-issue-156
   Scenario: Restore a file and check, if the content and correct checksum is now in the current file
@@ -143,246 +100,6 @@ Feature: dav-versions
     When user "Brian" sends HTTP method "PROPFIND" to URL "/remote.php/dav/meta/<<FILEID>>"
     Then the HTTP status code should be "400" or "404"
 
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: User can access meta folder of a file which is owned by somebody else but shared with that user
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "123" to "/davtest.txt"
-    And user "Alice" has uploaded file with content "456789" to "/davtest.txt"
-    And we save it into "FILEID"
-    When user "Alice" creates a share using the sharing API with settings
-      | path        | /davtest.txt |
-      | shareType   | user         |
-      | shareWith   | Brian        |
-      | permissions | read         |
-    Then the HTTP status code should be "200"
-    And the version folder of fileId "<<FILEID>>" for user "Brian" should contain "1" element
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharer of a file can see the old version information when the sharee changes the content of the file
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
-    And user "Alice" has shared file "sharefile.txt" with user "Brian"
-    When user "Brian" has uploaded file with content "Second content" to "/sharefile.txt"
-    Then the HTTP status code should be "204"
-    And the version folder of file "/sharefile.txt" for user "Alice" should contain "1" element
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharer of a file can restore the original content of a shared file after the file has been modified by the sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
-    And user "Alice" has shared file "sharefile.txt" with user "Brian"
-    And user "Brian" has uploaded file with content "Second content" to "/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharer can restore a file inside a shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a file inside a shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a file inside a shared folder created by sharee and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a file inside a shared folder created by sharer and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a file inside a shared folder created by sharer and modified by sharer, when the folder has been moved by the sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved folder "/sharingfolder" to "/received/sharingfolder"
-    When user "Brian" restores version index "1" of file "/received/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a shared file created and modified by sharer, when the file has been moved by the sharee (file is at the top level of the sharer)
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "old content" to "/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharefile.txt"
-    And user "Alice" has shared file "/sharefile.txt" with user "Brian"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved file "/sharefile.txt" to "/received/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/received/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a shared file created and modified by sharer, when the file has been moved by the sharee (file is inside a folder of the sharer)
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has shared file "/sharingfolder/sharefile.txt" with user "Brian"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved file "/sharefile.txt" to "/received/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/received/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @issue-ocis-1289 @issue-ocis-1321 @notToImplementOnOCIS
-  Scenario: sharer can restore a file inside a group shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Carol" has been created with default attributes and without skeleton files
-    And group "grp1" has been created
-    And user "Brian" has been added to group "grp1"
-    And user "Carol" has been added to group "grp1"
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with group "grp1"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    And user "Carol" has uploaded file with content "Third content" to "/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "2" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Carol" should be "First content"
-
-  @files_sharing-app-required @notToImplementOnOCIS @issue-ocis-1238
-  Scenario Outline: Moving a file (with versions) into a shared folder as the sharee and as the sharer
-    Given using <dav_version> DAV path
-    And user "Brian" has been created with default attributes and without skeleton files
-    And user "Brian" has created folder "/testshare"
-    And user "Brian" has created a share with settings
-      | path        | testshare |
-      | shareType   | user      |
-      | permissions | change    |
-      | shareWith   | Alice     |
-    And user "<mover>" has uploaded file with content "test data 1" to "/testfile.txt"
-    And user "<mover>" has uploaded file with content "test data 2" to "/testfile.txt"
-    And user "<mover>" has uploaded file with content "test data 3" to "/testfile.txt"
-    When user "<mover>" moves file "/testfile.txt" to "/testshare/testfile.txt" using the WebDAV API
-    Then the HTTP status code should be "201"
-    And the content of file "/testshare/testfile.txt" for user "Alice" should be "test data 3"
-    And the content of file "/testshare/testfile.txt" for user "Brian" should be "test data 3"
-    And as "<mover>" file "/testfile.txt" should not exist
-    And the version folder of file "/testshare/testfile.txt" for user "Alice" should contain "2" elements
-    And the version folder of file "/testshare/testfile.txt" for user "Brian" should contain "2" elements
-    Examples:
-      | dav_version | mover |
-      | old         | Alice |
-      | new         | Alice |
-      | old         | Brian |
-      | new         | Brian |
-
-  @files_sharing-app-required @notToImplementOnOCIS @issue-ocis-1238
-  Scenario Outline: Moving a file (with versions) out of a shared folder as the sharee and as the sharer
-    Given using <dav_version> DAV path
-    And user "Brian" has been created with default attributes and without skeleton files
-    And user "Brian" has created folder "/testshare"
-    And user "Brian" has uploaded file with content "test data 1" to "/testshare/testfile.txt"
-    And user "Brian" has uploaded file with content "test data 2" to "/testshare/testfile.txt"
-    And user "Brian" has uploaded file with content "test data 3" to "/testshare/testfile.txt"
-    And user "Brian" has created a share with settings
-      | path        | testshare |
-      | shareType   | user      |
-      | permissions | change    |
-      | shareWith   | Alice     |
-    When user "<mover>" moves file "/testshare/testfile.txt" to "/testfile.txt" using the WebDAV API
-    Then the HTTP status code should be "201"
-    And the content of file "/testfile.txt" for user "<mover>" should be "test data 3"
-    And as "Alice" file "/testshare/testfile.txt" should not exist
-    And as "Brian" file "/testshare/testfile.txt" should not exist
-    And the version folder of file "/testfile.txt" for user "<mover>" should contain "2" elements
-    Examples:
-      | dav_version | mover |
-      | old         | Alice |
-      | new         | Alice |
-      | old         | Brian |
-      | new         | Brian |
-
-  @files_sharing-app-required @issue-ocis-1238 @notToImplementOnOCIS
-  Scenario: Receiver tries to get file versions of unshared file from the sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "textfile1" to "textfile1.txt"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    When user "Brian" tries to get versions of file "textfile1.txt" from "Alice"
-    Then the HTTP status code should be "404"
-    And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\NotFound"
-
-  @skipOnStorage:ceph @files_primary_s3-issue-161 @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: Receiver tries get file versions of shared file from the sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 3" to "textfile0.txt"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
-    Then the HTTP status code should be "207"
-    And the number of versions should be "3"
-
 
   Scenario: User cannot access meta folder of a file which does not exist
     Given user "Brian" has been created with default attributes and without skeleton files
@@ -553,4 +270,259 @@ Feature: dav-versions
     And user "Alice" has uploaded file "filesForUpload/textfile.txt" to "file.txt" with mtime "Thu, 08 Aug 2019 04:18:13 GMT" using the WebDAV API
     When user "Alice" restores version index "1" of file "/file.txt" using the WebDAV API
     Then the HTTP status code should be "204"
-    And the version folder of file "/file.txt" for user "Alice" should contain "0" element
\ No newline at end of file
+    And the version folder of file "/file.txt" for user "Alice" should contain "0" element
+
+  @files_sharing-app-required
+  Scenario: User can access meta folder of a file which is owned by somebody else but shared with that user
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "123" to "/davtest.txt"
+    And user "Alice" has uploaded file with content "456789" to "/davtest.txt"
+    And we save it into "FILEID"
+    And user "Alice" has created a share with settings
+      | path        | /davtest.txt |
+      | shareType   | user         |
+      | shareWith   | Brian        |
+      | permissions | read         |
+    When user "Brian" accepts share "/davtest.txt" offered by user "Alice" using the sharing API
+    Then the HTTP status code should be "200"
+    And the version folder of fileId "<<FILEID>>" for user "Brian" should contain "1" element
+
+  @files_sharing-app-required
+  Scenario: sharer of a file can see the old version information when the sharee changes the content of the file
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
+    And user "Alice" has shared file "sharefile.txt" with user "Brian"
+    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
+    When user "Brian" uploads file with content "Second content" to "/Shares/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the version folder of file "/Shares/sharefile.txt" for user "Brian" should contain "1" element
+    And the version folder of file "/sharefile.txt" for user "Alice" should contain "1" element
+
+  @files_sharing-app-required
+  Scenario: sharer of a file can restore the original content of a shared file after the file has been modified by the sharee
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
+    And user "Alice" has shared file "sharefile.txt" with user "Brian"
+    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
+    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharefile.txt"
+    When user "Alice" restores version index "1" of file "/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharefile.txt" for user "Brian" should be "First content"
+
+  @files_sharing-app-required
+  Scenario: sharer can restore a file inside a shared folder modified by sharee
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
+    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
+    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
+
+  @files_sharing-app-required
+  Scenario: sharee can restore a file inside a shared folder modified by sharee
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
+    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
+    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
+
+  @files_sharing-app-required
+  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharer
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Brian" has uploaded file with content "First content" to "/Shares/sharingfolder/sharefile.txt"
+    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
+    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
+
+  @files_sharing-app-required
+  Scenario: sharee can restore a file inside a shared folder created by sharee and modified by sharer
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Brian" has uploaded file with content "First content" to "/Shares/sharingfolder/sharefile.txt"
+    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
+    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
+
+  @files_sharing-app-required
+  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharee
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Brian" has uploaded file with content "old content" to "/Shares/sharingfolder/sharefile.txt"
+    And user "Brian" has uploaded file with content "new content" to "/Shares/sharingfolder/sharefile.txt"
+    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
+
+  @files_sharing-app-required
+  Scenario: sharee can restore a file inside a shared folder created by sharer and modified by sharer
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
+    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
+    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
+
+  @files_sharing-app-required @issue-ocis-reva-34
+  Scenario: sharer can restore a file inside a group shared folder modified by sharee
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Carol" has been created with default attributes and without skeleton files
+    And group "grp1" has been created
+    And user "Brian" has been added to group "grp1"
+    And user "Carol" has been added to group "grp1"
+    And user "Alice" has created folder "/sharingfolder"
+    And user "Alice" has shared folder "/sharingfolder" with group "grp1"
+    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Carol" has accepted share "/sharingfolder" offered by user "Alice"
+    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
+    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
+    And user "Carol" has uploaded file with content "Third content" to "/Shares/sharingfolder/sharefile.txt"
+    When user "Alice" restores version index "2" of file "/sharingfolder/sharefile.txt" using the WebDAV API
+    Then the HTTP status code should be "204"
+    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
+    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Carol" should be "First content"
+
+  @files_sharing-app-required @issue-ocis-reva-386
+  Scenario Outline: Moving a file (with versions) into a shared folder as the sharee and as the sharer
+    Given using <dav_version> DAV path
+    And user "Brian" has been created with default attributes and without skeleton files
+    And user "Brian" has created folder "/testshare"
+    And user "Brian" has created a share with settings
+      | path        | testshare |
+      | shareType   | user      |
+      | permissions | change    |
+      | shareWith   | Alice     |
+    And user "Alice" has accepted share "/testshare" offered by user "Brian"
+    And user "<mover>" has uploaded file with content "test data 1" to "/testfile.txt"
+    And user "<mover>" has uploaded file with content "test data 2" to "/testfile.txt"
+    And user "<mover>" has uploaded file with content "test data 3" to "/testfile.txt"
+    When user "<mover>" moves file "/testfile.txt" to "<dst-folder>/testfile.txt" using the WebDAV API
+    Then the HTTP status code should be "201"
+    And the content of file "/Shares/testshare/testfile.txt" for user "Alice" should be "test data 3"
+    And the content of file "/testshare/testfile.txt" for user "Brian" should be "test data 3"
+    And as "<mover>" file "/testfile.txt" should not exist
+    And the version folder of file "/Shares/testshare/testfile.txt" for user "Alice" should contain "2" elements
+    And the version folder of file "/testshare/testfile.txt" for user "Brian" should contain "2" elements
+    Examples:
+      | dav_version | mover | dst-folder        |
+      | old         | Brian | /testshare        |
+      | new         | Brian | /testshare        |
+
+  @files_sharing-app-required @issue-ocis-reva-386
+  Scenario Outline: Moving a file (with versions) out of a shared folder as the sharee and as the sharer
+    Given using <dav_version> DAV path
+    And user "Brian" has been created with default attributes and without skeleton files
+    And user "Brian" has created folder "/testshare"
+    And user "Brian" has uploaded file with content "test data 1" to "/testshare/testfile.txt"
+    And user "Brian" has uploaded file with content "test data 2" to "/testshare/testfile.txt"
+    And user "Brian" has uploaded file with content "test data 3" to "/testshare/testfile.txt"
+    And user "Brian" has created a share with settings
+      | path        | testshare |
+      | shareType   | user      |
+      | permissions | change    |
+      | shareWith   | Alice     |
+    And user "Alice" has accepted share "/testshare" offered by user "Brian"
+    When user "<mover>" moves file "<src-folder>/testfile.txt" to "/testfile.txt" using the WebDAV API
+    Then the HTTP status code should be "201"
+    And the content of file "/testfile.txt" for user "<mover>" should be "test data 3"
+    And as "Alice" file "/Shares/testshare/testfile.txt" should not exist
+    And as "Brian" file "/testshare/testfile.txt" should not exist
+    And the version folder of file "/testfile.txt" for user "<mover>" should contain "2" elements
+    Examples:
+      | dav_version | mover | src-folder |
+      | old         | Brian | /testshare |
+      | new         | Brian | /testshare |
+
+  @files_sharing-app-required
+  Scenario: Receiver tries to get file versions of unshared file from the sharer
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "textfile1" to "textfile1.txt"
+    And user "Alice" has shared file "textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    When user "Brian" tries to get versions of file "textfile1.txt" from "Alice"
+    Then the HTTP status code should be "404"
+    And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\NotFound"
+
+  @skipOnStorage:ceph @files_primary_s3-issue-161 @files_sharing-app-required
+  Scenario: Receiver tries get file versions of shared file from the sharer
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 3" to "textfile0.txt"
+    And user "Alice" has shared file "textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
+    Then the HTTP status code should be "207"
+    And the number of versions should be "3"
+
+  @issue-760
+  Scenario: Receiver tries get file versions of shared file before receiving it
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
+    And we save it into "FILEID"
+    And user "Alice" has shared file "textfile0.txt" with user "Brian"
+    When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
+    Then the HTTP status code should be "404"
+    And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\NotFound"
+
+
+  Scenario: sharer tries get file versions of shared file when the sharee changes the content of the file
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
+    And user "Alice" has shared file "sharefile.txt" with user "Brian"
+    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
+    When user "Brian" has uploaded file with content "Second content" to "/Shares/sharefile.txt"
+    Then the HTTP status code should be "204"
+    And the version folder of file "/Shares/sharefile.txt" for user "Brian" should contain "1" element
+    And the version folder of file "/sharefile.txt" for user "Alice" should contain "1" element
+
+
+  Scenario: download old versions of a shared file as share receiver
+    Given user "Brian" has been created with default attributes and without skeleton files
+    And user "Alice" has uploaded file with content "uploaded content" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
+    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
+    And user "Alice" has shared file "textfile0.txt" with user "Brian"
+    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
+    When user "Brian" downloads the version of file "/Shares/textfile0.txt" with the index "1"
+    Then the HTTP status code should be "200"
+    And the following headers should be set
+      | header              | value                                                                |
+      | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" |
+    And the downloaded content should be "version 1"
+    When user "Brian" downloads the version of file "/Shares/textfile0.txt" with the index "2"
+    Then the HTTP status code should be "200"
+    And the following headers should be set
+      | header              | value                                                                |
+      | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" |
+    And the downloaded content should be "uploaded content"
\ No newline at end of file
diff --git a/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature b/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature
deleted file mode 100644
index c53de028988..00000000000
--- a/tests/acceptance/features/coreApiVersions/fileVersionsSharingToShares.feature
+++ /dev/null
@@ -1,324 +0,0 @@
-@api @files_versions-app-required @issue-ocis-reva-275
-
-Feature: dav-versions
-
-  Background:
-    Given using OCS API version "2"
-    And using new DAV path
-    And the administrator has set the default folder for received shares to "Shares"
-    And auto-accept shares has been disabled
-    And user "Alice" has been created with default attributes and without skeleton files
-
-
-  Scenario: Upload file and no version is available
-    When user "Alice" uploads file "filesForUpload/davtest.txt" to "/davtest.txt" using the WebDAV API
-    Then the version folder of file "/davtest.txt" for user "Alice" should contain "0" elements
-
-  @files_sharing-app-required
-  Scenario: User can access meta folder of a file which is owned by somebody else but shared with that user
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "123" to "/davtest.txt"
-    And user "Alice" has uploaded file with content "456789" to "/davtest.txt"
-    And we save it into "FILEID"
-    And user "Alice" has created a share with settings
-      | path        | /davtest.txt |
-      | shareType   | user         |
-      | shareWith   | Brian        |
-      | permissions | read         |
-    When user "Brian" accepts share "/davtest.txt" offered by user "Alice" using the sharing API
-    Then the HTTP status code should be "200"
-    And the version folder of fileId "<<FILEID>>" for user "Brian" should contain "1" element
-
-  @files_sharing-app-required
-  Scenario: sharer of a file can see the old version information when the sharee changes the content of the file
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
-    And user "Alice" has shared file "sharefile.txt" with user "Brian"
-    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
-    When user "Brian" uploads file with content "Second content" to "/Shares/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the version folder of file "/Shares/sharefile.txt" for user "Brian" should contain "1" element
-    And the version folder of file "/sharefile.txt" for user "Alice" should contain "1" element
-
-  @files_sharing-app-required
-  Scenario: sharer of a file can restore the original content of a shared file after the file has been modified by the sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
-    And user "Alice" has shared file "sharefile.txt" with user "Brian"
-    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
-    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required
-  Scenario: sharer can restore a file inside a shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required
-  Scenario: sharee can restore a file inside a shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required
-  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Brian" has uploaded file with content "First content" to "/Shares/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required
-  Scenario: sharee can restore a file inside a shared folder created by sharee and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Brian" has uploaded file with content "First content" to "/Shares/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "Second content" to "/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-
-  @files_sharing-app-required
-  Scenario: sharer can restore a file inside a shared folder created by sharee and modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Brian" has uploaded file with content "old content" to "/Shares/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "new content" to "/Shares/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "1" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required
-  Scenario: sharee can restore a file inside a shared folder created by sharer and modified by sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/Shares/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a file inside a shared folder created by sharer and modified by sharer, when the folder has been moved by the sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with user "Brian"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved folder "/Shares/sharingfolder" to "/received/sharingfolder"
-    When user "Brian" restores version index "1" of file "/received/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharingfolder/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a shared file created and modified by sharer, when the file has been moved by the sharee (file is at the top level of the sharer)
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "old content" to "/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharefile.txt"
-    And user "Alice" has shared file "/sharefile.txt" with user "Brian"
-    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved file "/Shares/sharefile.txt" to "/received/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/received/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @notToImplementOnOCIS
-  Scenario: sharee can restore a shared file created and modified by sharer, when the file has been moved by the sharee (file is inside a folder of the sharer)
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has uploaded file with content "old content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has uploaded file with content "new content" to "/sharingfolder/sharefile.txt"
-    And user "Alice" has shared file "/sharingfolder/sharefile.txt" with user "Brian"
-    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
-    And user "Brian" has created folder "/received"
-    And user "Brian" has moved file "/Shares/sharefile.txt" to "/received/sharefile.txt"
-    When user "Brian" restores version index "1" of file "/received/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "old content"
-    And the content of file "/received/sharefile.txt" for user "Brian" should be "old content"
-
-  @files_sharing-app-required @issue-ocis-reva-34
-  Scenario: sharer can restore a file inside a group shared folder modified by sharee
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Carol" has been created with default attributes and without skeleton files
-    And group "grp1" has been created
-    And user "Brian" has been added to group "grp1"
-    And user "Carol" has been added to group "grp1"
-    And user "Alice" has created folder "/sharingfolder"
-    And user "Alice" has shared folder "/sharingfolder" with group "grp1"
-    And user "Brian" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Carol" has accepted share "/sharingfolder" offered by user "Alice"
-    And user "Alice" has uploaded file with content "First content" to "/sharingfolder/sharefile.txt"
-    And user "Brian" has uploaded file with content "Second content" to "/Shares/sharingfolder/sharefile.txt"
-    And user "Carol" has uploaded file with content "Third content" to "/Shares/sharingfolder/sharefile.txt"
-    When user "Alice" restores version index "2" of file "/sharingfolder/sharefile.txt" using the WebDAV API
-    Then the HTTP status code should be "204"
-    And the content of file "/sharingfolder/sharefile.txt" for user "Alice" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Brian" should be "First content"
-    And the content of file "/Shares/sharingfolder/sharefile.txt" for user "Carol" should be "First content"
-
-  @files_sharing-app-required @issue-ocis-reva-386
-  Scenario Outline: Moving a file (with versions) into a shared folder as the sharee and as the sharer
-    Given using <dav_version> DAV path
-    And user "Brian" has been created with default attributes and without skeleton files
-    And user "Brian" has created folder "/testshare"
-    And user "Brian" has created a share with settings
-      | path        | testshare |
-      | shareType   | user      |
-      | permissions | change    |
-      | shareWith   | Alice     |
-    And user "Alice" has accepted share "/testshare" offered by user "Brian"
-    And user "<mover>" has uploaded file with content "test data 1" to "/testfile.txt"
-    And user "<mover>" has uploaded file with content "test data 2" to "/testfile.txt"
-    And user "<mover>" has uploaded file with content "test data 3" to "/testfile.txt"
-    When user "<mover>" moves file "/testfile.txt" to "<dst-folder>/testfile.txt" using the WebDAV API
-    Then the HTTP status code should be "201"
-    And the content of file "/Shares/testshare/testfile.txt" for user "Alice" should be "test data 3"
-    And the content of file "/testshare/testfile.txt" for user "Brian" should be "test data 3"
-    And as "<mover>" file "/testfile.txt" should not exist
-    And the version folder of file "/Shares/testshare/testfile.txt" for user "Alice" should contain "2" elements
-    And the version folder of file "/testshare/testfile.txt" for user "Brian" should contain "2" elements
-    Examples:
-      | dav_version | mover | dst-folder        |
-      | old         | Alice | /Shares/testshare |
-      | new         | Alice | /Shares/testshare |
-      | old         | Brian | /testshare        |
-      | new         | Brian | /testshare        |
-
-  @files_sharing-app-required @issue-ocis-reva-386
-  Scenario Outline: Moving a file (with versions) out of a shared folder as the sharee and as the sharer
-    Given using <dav_version> DAV path
-    And user "Brian" has been created with default attributes and without skeleton files
-    And user "Brian" has created folder "/testshare"
-    And user "Brian" has uploaded file with content "test data 1" to "/testshare/testfile.txt"
-    And user "Brian" has uploaded file with content "test data 2" to "/testshare/testfile.txt"
-    And user "Brian" has uploaded file with content "test data 3" to "/testshare/testfile.txt"
-    And user "Brian" has created a share with settings
-      | path        | testshare |
-      | shareType   | user      |
-      | permissions | change    |
-      | shareWith   | Alice     |
-    And user "Alice" has accepted share "/testshare" offered by user "Brian"
-    When user "<mover>" moves file "<src-folder>/testfile.txt" to "/testfile.txt" using the WebDAV API
-    Then the HTTP status code should be "201"
-    And the content of file "/testfile.txt" for user "<mover>" should be "test data 3"
-    And as "Alice" file "/Shares/testshare/testfile.txt" should not exist
-    And as "Brian" file "/testshare/testfile.txt" should not exist
-    And the version folder of file "/testfile.txt" for user "<mover>" should contain "2" elements
-
-    @notToImplementOnOCIS
-    Examples:
-      | dav_version | mover | src-folder        |
-      | old         | Alice | /Shares/testshare |
-      | new         | Alice | /Shares/testshare |
-
-
-    Examples:
-      | dav_version | mover | src-folder |
-      | old         | Brian | /testshare |
-      | new         | Brian | /testshare |
-
-  @files_sharing-app-required
-  Scenario: Receiver tries to get file versions of unshared file from the sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "textfile1" to "textfile1.txt"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
-    When user "Brian" tries to get versions of file "textfile1.txt" from "Alice"
-    Then the HTTP status code should be "404"
-    And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\NotFound"
-
-  @skipOnStorage:ceph @files_primary_s3-issue-161 @files_sharing-app-required
-  Scenario: Receiver tries get file versions of shared file from the sharer
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 3" to "textfile0.txt"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
-    When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
-    Then the HTTP status code should be "207"
-    And the number of versions should be "3"
-
-
-  Scenario: Receiver tries get file versions of shared file before receiving it
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "textfile0" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
-    And we save it into "FILEID"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    When user "Brian" tries to get versions of file "textfile0.txt" from "Alice"
-    Then the HTTP status code should be "404"
-    And the value of the item "//s:exception" in the response about user "Alice" should be "Sabre\DAV\Exception\NotFound"
-
-
-  Scenario: sharer tries get file versions of shared file when the sharee changes the content of the file
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "First content" to "sharefile.txt"
-    And user "Alice" has shared file "sharefile.txt" with user "Brian"
-    And user "Brian" has accepted share "/sharefile.txt" offered by user "Alice"
-    When user "Brian" has uploaded file with content "Second content" to "/Shares/sharefile.txt"
-    Then the HTTP status code should be "204"
-    And the version folder of file "/Shares/sharefile.txt" for user "Brian" should contain "1" element
-    And the version folder of file "/sharefile.txt" for user "Alice" should contain "1" element
-
-
-  Scenario: download old versions of a shared file as share receiver
-    Given user "Brian" has been created with default attributes and without skeleton files
-    And user "Alice" has uploaded file with content "uploaded content" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 1" to "textfile0.txt"
-    And user "Alice" has uploaded file with content "version 2" to "textfile0.txt"
-    And user "Alice" has shared file "textfile0.txt" with user "Brian"
-    And user "Brian" has accepted share "/textfile0.txt" offered by user "Alice"
-    When user "Brian" downloads the version of file "/Shares/textfile0.txt" with the index "1"
-    Then the HTTP status code should be "200"
-    And the following headers should be set
-      | header              | value                                                                |
-      | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" |
-    And the downloaded content should be "version 1"
-    When user "Brian" downloads the version of file "/Shares/textfile0.txt" with the index "2"
-    Then the HTTP status code should be "200"
-    And the following headers should be set
-      | header              | value                                                                |
-      | Content-Disposition | attachment; filename*=UTF-8''textfile0.txt; filename="textfile0.txt" |
-    And the downloaded content should be "uploaded content"