Repository: owncloud/ocis
Pull request #5393 (2023-01-12 15:35:41)

[docs-only] Properly describe docs/helpers and processes
References: #5377 (Content of the 'docs-stable-2.0' branch gets magically deleted)

The docs/helpers and the processes behind need a proper description and how to handle backports.

Language review welcomed.

Backport to the `stable-2.0` necessary.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76881d415e0..413de620928 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,10 +17,8 @@ The following sections list the changes for unreleased.
 * Enhancement - Drive group permissions: [#5312](https://github.com/owncloud/ocis/pull/5312)
 * Enhancement - Make the group members addition limit configurable: [#5357](https://github.com/owncloud/ocis/pull/5357)
 * Enhancement - Graph Drives IdentitySet displayName: [#5347](https://github.com/owncloud/ocis/pull/5347)
-* Enhancement - Display surname and givenName attributes: [#5388](https://github.com/owncloud/ocis/pull/5388)
 * Enhancement - Extended search: [#5221](https://github.com/owncloud/ocis/pull/5221)
 * Enhancement - Resource tags: [#5227](https://github.com/owncloud/ocis/pull/5227)
-* Enhancement - Expiration Notifications: [#5330](https://github.com/owncloud/ocis/pull/5330)
 
 # Changelog for [2.0.0] (2022-11-30)
 
diff --git a/changelog/unreleased/enhancement-name-attributes.md b/changelog/unreleased/enhancement-name-attributes.md
deleted file mode 100644
index 6558b927eec..00000000000
--- a/changelog/unreleased/enhancement-name-attributes.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Enhancement: display surname and givenName attributes
-
-When querying the graph API, the surname and givenName attributes are now displayed for users.
-
-https://github.com/owncloud/ocis/pull/5388
-https://github.com/owncloud/ocis/issues/5386
diff --git a/changelog/unreleased/expiration-notifications.md b/changelog/unreleased/expiration-notifications.md
deleted file mode 100644
index 06b16c0808d..00000000000
--- a/changelog/unreleased/expiration-notifications.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Enhancement: Expiration Notifications
-
-Send emails to the user informing that a share or a space membership expires.
-
-https://github.com/owncloud/ocis/pull/5330
diff --git a/docs/helpers/README.md b/docs/helpers/README.md
index 914ecd876f2..21835a9e7ee 100644
--- a/docs/helpers/README.md
+++ b/docs/helpers/README.md
@@ -1,44 +1,91 @@
 # Docs Helpers
 
-`docs/helpers` contains a small go program creating docs by extracting information from the code. It is manually started with `make docs-generate` or via the CI and has three main responsibilities:
-
-- Generate docs for envvars in config structs
-- Extract envvars that are not mentioned in config structs (aka "Rogue" envvars)
-- Generate docs for rogue envvars
-
-Output:
+   * [Introduction](#introduction)
+   * [Output Generated](#output)
+   * [Admin Doc Process](#admin-doc-process)
+   * [Branching](#branching)
+   * [Service-Dependent Output](#service-dependent-output)
+      * [Generate Envvar Docs for Config Structs](#generate-envvar-docs-for-config-structs)
+      * [Deprecation Process](#deprecation-process)
+   * [Global Envvars](#global-envvars)
+   * [Extended Envvars](#extended-envvars)
+      * [General Extended Envvars Info](#general-extended-envvars-info)
+      * [Extract Extended Envvars](#extract-extended-envvars)
+      * [Generate Extended Envvar Docs](#generate-extended-envvar-docs)
+   * [Backporting](#backporting)
+
+## Introduction
+
+`docs/helpers` contains small go programs creating docs by extracting information from the code. The `main.go` program is manually started with `make docs-generate` or via the CI. It calls the other required programs and has three main responsibilities:
+
+- Generate docs for envvars in config structs including deprecations if there are any.
+- Extract and generate docs for `extended` envvars that are not mentioned in config structs (aka "rogue" envvars).
+- Extract and generate docs for `global` envvars which occur in multiple services.
+
+## Output Generated
 
 - The generated yaml files can be found at: `docs/services/_includes` when running locally respectively in the `docs branch` after the CI has finished.
 - The generated adoc files can be found at: `docs/services/_includes/adoc` when running locally respectively in the `docs branch` after the CI has finished.
-- The file name for rouge envvars is named: `extended_configvars.adoc`.
+- The file name for global envvars is named: `global_configvars.adoc`.
+- The file name for extended envvars is named: `extended_configvars.adoc`.
 
-Admin doc process:
+## Admin Doc Process
 
-Whenever a build from the admin documentation is triggered, the files generated here are included into the build process and added in a proper manner defined by the admin documentation.
+Whenever a build from the [ocis admin](https://github.com/owncloud/docs-ocis) or any other related documentation is triggered, the files generated here are included into the build process and added in a proper manner defined by the admin documentation. The updated documentation will then show up on the [web](https://doc.owncloud.com/ocis/next/).
 
-Genreal info:
+## Branching
 
-"Rouge" envvars are variables that need to be present *before* the core or services are starting up as they depend on the info provided like path for config files etc. Therefore they are _not_ bound to services like other envvars do. 
+The following is valid for envvars and yaml files related to the doc process:
 
-It can happen that rouge envvars are found but do not need to be published as they are for internal use only. Those rouge envvars can be defined to be ignored for further processing.
+* When filing a pull request in the ocis master branch relating to docs, CI runs `make docs-generate` and copies the result into the `docs` branch of ocis. This branch is then taken as base for owncloud.dev and as reference for the [admin docs](https://doc.owncloud.com/ocis/next/).
+* When running `make docs-generate` locally, the same output is created as above but it stays in the same branch where the make command was issued.
 
-IMPORTANT:
+In both cases, `make docs-generate` removes files in the target folder `_includes` to avoid remnants. All content is recreated. 
 
-- Once a rouge envvar has been identified, it is added to the `extended_vars.yaml` file but never changed or touched by the process. There is one exception with respect to single/double quote usage. While you manually can (and will) define a text like: `"'/var/lib/ocis'"`, quotes are transformed by the process in the .yaml file to: `'''/var/lib/ocis'''`. There is no need to change this back, as the final step transforms this correctly to the adoc table.
+On a side note (unrelated to the `docs` branch), [deployment examples](https://github.com/owncloud/ocis/tree/master/deployments/examples) have their own branch related to an ocis stable version to keep the state consistent, which is necessary for the admin documentation.
 
-- Because rouge envvars do not have the same structural setup as "normal" envvars like type, description or defaults, these infos need to be provided manually one time - even if found multiple times. Any change on this info will be used on the next CI run and published on the next admin docs build.
+## Service-Dependent Output
 
-- Do not change the sort order of rouge envvar blocks as they are automatically reordered alphabetically.
+For each service available, a file named like `<service name>_configvars.adoc` is created containing a:
 
-## Generate Envvar docs for config structs
+* table on top defining deprecated envvars - if applicable
+* table containing all envvars with their name, type, default value and description
+
+The table with deprecations is always printed in the final adoc file even if there are none, but is rendered in the docs build process only if the `HasDeprecations` value is set. This value is automatically handed over via the adoc file. The template file can be found at `docs/templates/ADOC.tmpl`.
+
+### Generate Envvar Docs for Config Structs
 
 Generates docs from a template file, mainly extracting `"env"` and `"desc"` tags from the config structs.
 
 Templates can be found in `docs/helpers` folder. (Same as this `README`.) Check `.tmpl` files
 
-## Extract Rogue Envvars
+### Deprecation Process
+
+For details on deprecation see the [deprecating-variables](https://github.com/owncloud/ocis/blob/master/docs/ocis/development/deprecating-variables.md) documentation.
+
+## Global Envvars
+
+Global envvars are gathered by checking if the envvar is available in more than one service. The table created is similar to the service-dependent envvar table but additionally contains a column with all service names where this envvar occurs. The output is rendered in list form where each item is clickable and automatically points to the corresponding service page. The template file can be found at `docs/templates/ADOC_global.tmpl`.
+
+## Extended Envvars
+
+### General Extended Envvars Info
+
+"Extended" envvars are variables that need to be present *before* the core or services are starting up as they depend on the info provided like path for config files etc. Therefore they are _not_ bound to services like other envvars. 
 
-It `grep`s over the code, looking for `os.Getenv` and parses these contents to a yaml file along with the following information:
+It can happen that extended envvars are found but do not need to be published as they are for internal use only. Those envvars can be defined to be ignored for further processing.
+
+IMPORTANT:
+
+- Once a extended envvar has been identified, it is added to the `extended_vars.yaml` file found in this folder but never changed or touched by the process. There is one exception with respect to single/double quote usage. While you can (and will) manually define a text like: `"'/var/lib/ocis'"`, quotes are transformed by the process in the .yaml file to: `'''/var/lib/ocis'''`. There is no need to change this back, as the final step transforms this correctly for the adoc table.
+
+- Because extended envvars do not have the same structural setup as "normal" envvars (like type, description or defaults), this info needs to be provided manually once - even if found multiple times. Any change of this info will be noticed during the next CI run and published in the next admin docs build.
+
+- Do not change the sort order of extended envvar blocks as they are automatically reordered alphabetically.
+
+### Extract Extended Envvars
+
+The grep command parses the code, looking for `os.Getenv` and passes these contents to a yaml file along with the following information:
 ```golang
 // Variable contains all information about one rogue envvar
 type Variable struct {
@@ -70,8 +117,16 @@ This yaml file can later be manually edited to add descriptions, default values,
 
 IMPORTANT: `RawName`, `Path` and `FoundInCode` are automatically filled by the program. DO NOT EDIT THESE VALUES MANUALLY.
 
-## Generate Rogue Envvar docs
+### Generate Extended Envvar Docs
+
+The process further picks up the `yaml` file generated in the `Extract Rogue Envvars` step and renders it to an adoc file (a table is created) using a go template. The template file for this step can be found at `docs/templates/ADOC_extended.tmpl`.
+
+## Backporting
+
+The ocis repo contains branches which are necessary for the documentation. The `docs` branch is related to changes in master, necessary for owncloud.dev and the admin docs referencing master content when it comes to envvars and yaml files.
+
+When a new stable ocis release (branch) is published, like `stable-2.0`, an additional branch (including CI) is set up manually by the dev team for referencing docs content like `docs-stable-2.0` - related to envvars and yaml files only - and added to the CI.
 
-It picks up the `yaml` file generated in `Extract Rogue Envvars` step and renders it to a adoc file (table) using a go template.
+In case it is necessary to transport a change from master to a stable branch like `docs-stable-2.0`, you must backport the original changes that will create that file to the `stable-2.0` branch. The CI will then take care of creating the results in the target `docs-stable-2.0`.
 
-The adoc template file for this step can be found at `docs/templates/ADOC_global.tmpl`.
+Cases for a backport can be a typo in an envvar description you want to have fixed in a stable branch too or a file  was created after the stable branch was set up but needs to be available in that branch.
diff --git a/go.mod b/go.mod
index 2964562fce2..7518b1b1eb8 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.20230112100946-f8c784168ef7
+	github.com/cs3org/reva/v2 v2.12.1-0.20230110081316-da324d280315
 	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.1
 	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 351c458f36f..1d9711e038b 100644
--- a/go.sum
+++ b/go.sum
@@ -344,8 +344,8 @@ 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/go-cs3apis v0.0.0-20221012090518-ef2996678965 h1:y4n2j68LLnvac+zw/al8MfPgO5aQiIwLmHM/JzYN8AM=
 github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
-github.com/cs3org/reva/v2 v2.12.1-0.20230112100946-f8c784168ef7 h1:xULPUJdydKJbZ8z0MD4zkOqnz2oE+SXKw+3GzSYr+yY=
-github.com/cs3org/reva/v2 v2.12.1-0.20230112100946-f8c784168ef7/go.mod h1:GpocVB1w6yxeSr1VBsO9jztmt1SyNC4lCwudLwDzxHQ=
+github.com/cs3org/reva/v2 v2.12.1-0.20230110081316-da324d280315 h1:pfOiXbiYW9ZPwpLOxtzSOuocanSoplhi8NE4KbQxhbk=
+github.com/cs3org/reva/v2 v2.12.1-0.20230110081316-da324d280315/go.mod h1:GpocVB1w6yxeSr1VBsO9jztmt1SyNC4lCwudLwDzxHQ=
 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=
@@ -873,8 +873,6 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
 github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
-github.com/kobergj/reva/v2 v2.0.0-20230112095039-1aba27929ce4 h1:jvN+oN9vHYXhrYv76OJ6bhXx7ldDpErblPZSwu0UzmA=
-github.com/kobergj/reva/v2 v2.0.0-20230112095039-1aba27929ce4/go.mod h1:GpocVB1w6yxeSr1VBsO9jztmt1SyNC4lCwudLwDzxHQ=
 github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -1060,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.1 h1:wj3aQQr/yDPoc97ddg7DCadvMx6ui6N7re/oRV9+yNs=
+github.com/owncloud/libre-graph-api-go v1.0.1/go.mod h1:579sFrPP7aP24LZXGPopLfvE+hAka/2DYHk0+Ij+w+U=
 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/ocis/pkg/init/init.go b/ocis/pkg/init/init.go
index 560af63144d..3663172a0ec 100644
--- a/ocis/pkg/init/init.go
+++ b/ocis/pkg/init/init.go
@@ -50,15 +50,11 @@ type LdapBasedService struct {
 type Events struct {
 	TLSInsecure bool `yaml:"tls_insecure"`
 }
-type GraphApplication struct {
-	ID string `yaml:"id"`
-}
 
 type GraphService struct {
-	Application GraphApplication
-	Events      Events
-	Spaces      InsecureService
-	Identity    LdapBasedService
+	Events   Events
+	Spaces   InsecureService
+	Identity LdapBasedService
 }
 
 type ServiceUserPasswordsSettings struct {
@@ -223,7 +219,6 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 
 	systemUserID := uuid.Must(uuid.NewV4()).String()
 	adminUserID := uuid.Must(uuid.NewV4()).String()
-	graphApplicationID := uuid.Must(uuid.NewV4()).String()
 	storageUsersMountID := uuid.Must(uuid.NewV4()).String()
 
 	idmServicePassword, err := generators.GenerateRandomPassword(passwordLength)
@@ -311,9 +306,6 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 			},
 		},
 		Graph: GraphService{
-			Application: GraphApplication{
-				ID: graphApplicationID,
-			},
 			Identity: LdapBasedService{
 				Ldap: LdapSettings{
 					BindPassword: idmServicePassword,
diff --git a/services/graph/pkg/config/application.go b/services/graph/pkg/config/application.go
deleted file mode 100644
index c33af28652c..00000000000
--- a/services/graph/pkg/config/application.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package config
-
-// Application defines the available graph application configuration.
-type Application struct {
-	ID          string `yaml:"id" env:"GRAPH_APPLICATION_ID" desc:"The ocis application id shown in the graph. All app roles are tied to this."`
-	DisplayName string `yaml:"displayname" env:"GRAPH_APPLICATION_DISPLAYNAME" desc:"The oCIS application name"`
-}
diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go
index 5b2f00e5189..b3618c72e96 100644
--- a/services/graph/pkg/config/config.go
+++ b/services/graph/pkg/config/config.go
@@ -25,10 +25,9 @@ type Config struct {
 	TokenManager  *TokenManager         `yaml:"token_manager"`
 	GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
 
-	Application Application `yaml:"application"`
-	Spaces      Spaces      `yaml:"spaces"`
-	Identity    Identity    `yaml:"identity"`
-	Events      Events      `yaml:"events"`
+	Spaces   Spaces   `yaml:"spaces"`
+	Identity Identity `yaml:"identity"`
+	Events   Events   `yaml:"events"`
 
 	Context context.Context `yaml:"-"`
 }
diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go
index 023efe04939..1d06dad3fdb 100644
--- a/services/graph/pkg/config/defaults/defaultconfig.go
+++ b/services/graph/pkg/config/defaults/defaultconfig.go
@@ -31,9 +31,6 @@ func DefaultConfig() *config.Config {
 		Service: config.Service{
 			Name: "graph",
 		},
-		Application: config.Application{
-			DisplayName: "ownCloud Infinite Scale",
-		},
 		API: config.API{
 			GroupMembersPatchLimit: 20,
 		},
diff --git a/services/graph/pkg/config/parser/parse.go b/services/graph/pkg/config/parser/parse.go
index ffd91247ea8..d0fa4780b82 100644
--- a/services/graph/pkg/config/parser/parse.go
+++ b/services/graph/pkg/config/parser/parse.go
@@ -2,10 +2,8 @@ package parser
 
 import (
 	"errors"
-	"fmt"
 
 	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
-	defaults2 "github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
 	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/services/graph/pkg/config"
 	"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
@@ -44,13 +42,5 @@ func Validate(cfg *config.Config) error {
 		return shared.MissingLDAPBindPassword(cfg.Service.Name)
 	}
 
-	if cfg.Application.ID == "" {
-		return fmt.Errorf("The application ID has not been configured for %s. "+
-			"Make sure your %s config contains the proper values "+
-			"(e.g. by running ocis init or setting it manually in "+
-			"the config/corresponding environment variable).",
-			"graph", defaults2.BaseConfigPath())
-	}
-
 	return nil
 }
diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go
index 735f4ee6a4d..9f96812ad74 100644
--- a/services/graph/pkg/identity/ldap.go
+++ b/services/graph/pkg/identity/ldap.go
@@ -18,11 +18,6 @@ import (
 	"golang.org/x/exp/slices"
 )
 
-const (
-	givenNameAttribute = "givenname"
-	surNameAttribute   = "sn"
-)
-
 type LDAP struct {
 	useServerUUID   bool
 	writeEnabled    bool
@@ -51,8 +46,6 @@ type userAttributeMap struct {
 	id          string
 	mail        string
 	userName    string
-	givenName   string
-	surname     string
 }
 
 type groupAttributeMap struct {
@@ -74,8 +67,6 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
 		id:          config.UserIDAttribute,
 		mail:        config.UserEmailAttribute,
 		userName:    config.UserNameAttribute,
-		givenName:   givenNameAttribute,
-		surname:     surNameAttribute,
 	}
 
 	if config.GroupNameAttribute == "" || config.GroupIDAttribute == "" {
@@ -275,8 +266,6 @@ func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) {
 		i.userAttributeMap.id,
 		i.userAttributeMap.mail,
 		i.userAttributeMap.userName,
-		i.userAttributeMap.surname,
-		i.userAttributeMap.givenName,
 	}
 
 	filter := fmt.Sprintf("(objectClass=%s)", i.userObjectClass)
@@ -384,8 +373,6 @@ func (i *LDAP) getLDAPUserByFilter(filter string) (*ldap.Entry, error) {
 		i.userAttributeMap.id,
 		i.userAttributeMap.mail,
 		i.userAttributeMap.userName,
-		i.userAttributeMap.surname,
-		i.userAttributeMap.givenName,
 	}
 	return i.searchLDAPEntryByFilter(i.userBaseDN, attrs, filter)
 }
@@ -443,8 +430,6 @@ func (i *LDAP) GetUsers(ctx context.Context, queryParam url.Values) ([]*libregra
 			i.userAttributeMap.id,
 			i.userAttributeMap.mail,
 			i.userAttributeMap.userName,
-			i.userAttributeMap.surname,
-			i.userAttributeMap.givenName,
 		},
 		nil,
 	)
@@ -947,8 +932,6 @@ func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
 
 	opsan := e.GetEqualFoldAttributeValue(i.userAttributeMap.userName)
 	id := e.GetEqualFoldAttributeValue(i.userAttributeMap.id)
-	givenName := e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName)
-	surname := e.GetEqualFoldAttributeValue(i.userAttributeMap.surname)
 
 	if id != "" && opsan != "" {
 		return &libregraph.User{
@@ -956,8 +939,6 @@ func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
 			Mail:                     pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.mail)),
 			OnPremisesSamAccountName: &opsan,
 			Id:                       &id,
-			GivenName:                &givenName,
-			Surname:                  &surname,
 		}
 	}
 	i.logger.Warn().Str("dn", e.DN).Msg("Invalid User. Missing username or id attribute")
@@ -1010,11 +991,11 @@ func (i *LDAP) userToLDAPAttrValues(user libregraph.User) (map[string][]string,
 	} else {
 		sn = *user.OnPremisesSamAccountName
 	}
-	attrs[i.userAttributeMap.surname] = []string{sn}
+	attrs["sn"] = []string{sn}
 
 	// When we get a givenName, we set the attribute.
 	if givenName := user.GetGivenName(); givenName != "" {
-		attrs[i.userAttributeMap.givenName] = []string{givenName}
+		attrs["givenname"] = []string{givenName}
 	}
 
 	if !i.usePwModifyExOp && user.PasswordProfile != nil && user.PasswordProfile.Password != nil {
diff --git a/services/graph/pkg/service/v0/application.go b/services/graph/pkg/service/v0/application.go
deleted file mode 100644
index c7c7c7a51c3..00000000000
--- a/services/graph/pkg/service/v0/application.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package svc
-
-import (
-	"fmt"
-	"net/http"
-
-	"github.com/go-chi/chi/v5"
-	"github.com/go-chi/render"
-	libregraph "github.com/owncloud/libre-graph-api-go"
-	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
-)
-
-// ListApplications implements the Service interface.
-func (g Graph) ListApplications(w http.ResponseWriter, r *http.Request) {
-	logger := g.logger.SubloggerWithRequestID(r.Context())
-	logger.Info().Interface("query", r.URL.Query()).Msg("calling list applications")
-
-	lbr, err := g.roleService.ListRoles(r.Context(), &settingssvc.ListBundlesRequest{})
-	if err != nil {
-		logger.Error().Err(err).Msg("could not list roles: transport error")
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	roles := make([]libregraph.AppRole, 0, len(lbr.Bundles))
-	for _, bundle := range lbr.GetBundles() {
-		role := libregraph.NewAppRole(bundle.GetId())
-		role.SetDisplayName(bundle.GetDisplayName())
-		roles = append(roles, *role)
-	}
-
-	application := libregraph.NewApplication(g.config.Application.ID)
-	application.SetDisplayName(g.config.Application.DisplayName)
-	application.SetAppRoles(roles)
-
-	applications := []*libregraph.Application{
-		application,
-	}
-
-	render.Status(r, http.StatusOK)
-	render.JSON(w, r, &ListResponse{Value: applications})
-}
-
-// GetApplication implements the Service interface.
-func (g Graph) GetApplication(w http.ResponseWriter, r *http.Request) {
-	logger := g.logger.SubloggerWithRequestID(r.Context())
-	logger.Info().Interface("query", r.URL.Query()).Msg("calling get application")
-
-	applicationID := chi.URLParam(r, "applicationID")
-
-	if applicationID != g.config.Application.ID {
-		errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, fmt.Sprintf("requested id %s does not match expected application id %v", applicationID, g.config.Application.ID))
-		return
-	}
-
-	lbr, err := g.roleService.ListRoles(r.Context(), &settingssvc.ListBundlesRequest{})
-	if err != nil {
-		logger.Error().Err(err).Msg("could not list roles: transport error")
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	roles := make([]libregraph.AppRole, 0, len(lbr.Bundles))
-	for _, bundle := range lbr.GetBundles() {
-		role := libregraph.NewAppRole(bundle.GetId())
-		role.SetDisplayName(bundle.GetDisplayName())
-		roles = append(roles, *role)
-	}
-
-	application := libregraph.NewApplication(applicationID)
-	application.SetDisplayName(g.config.Application.DisplayName)
-	application.SetAppRoles(roles)
-
-	render.Status(r, http.StatusOK)
-	render.JSON(w, r, application)
-}
diff --git a/services/graph/pkg/service/v0/approleassignments.go b/services/graph/pkg/service/v0/approleassignments.go
deleted file mode 100644
index 849193cd61b..00000000000
--- a/services/graph/pkg/service/v0/approleassignments.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package svc
-
-import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-
-	"github.com/go-chi/chi/v5"
-	"github.com/go-chi/render"
-	libregraph "github.com/owncloud/libre-graph-api-go"
-	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
-)
-
-const principalTypeUser = "User"
-
-// ListAppRoleAssignments implements the Service interface.
-func (g Graph) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
-	logger := g.logger.SubloggerWithRequestID(r.Context())
-	logger.Info().Interface("query", r.URL.Query()).Msg("calling list appRoleAssignments")
-
-	userID := chi.URLParam(r, "userID")
-
-	lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settingssvc.ListRoleAssignmentsRequest{
-		AccountUuid: userID,
-	})
-	if err != nil {
-		// TODO check the error type and return proper error code
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
-	for _, assignment := range lrar.GetAssignments() {
-		values = append(values, g.assignmentToAppRoleAssignment(assignment))
-	}
-
-	render.Status(r, http.StatusOK)
-	render.JSON(w, r, &ListResponse{Value: values})
-}
-
-// CreateAppRoleAssignment implements the Service interface.
-func (g Graph) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	logger := g.logger.SubloggerWithRequestID(r.Context())
-	logger.Info().Interface("query", r.URL.Query()).Msg("calling create appRoleAssignment")
-
-	appRoleAssignment := libregraph.NewAppRoleAssignmentWithDefaults()
-	err := json.NewDecoder(r.Body).Decode(appRoleAssignment)
-	if err != nil {
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err.Error()))
-		return
-	}
-
-	userID := chi.URLParam(r, "userID")
-
-	if appRoleAssignment.GetPrincipalId() != userID {
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("user id %s does not match principal id %v", userID, appRoleAssignment.GetPrincipalId()))
-		return
-	}
-	if appRoleAssignment.GetResourceId() != g.config.Application.ID {
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("resource id %s does not match expected application id %v", userID, g.config.Application.ID))
-		return
-	}
-
-	artur, err := g.roleService.AssignRoleToUser(r.Context(), &settingssvc.AssignRoleToUserRequest{
-		AccountUuid: userID,
-		RoleId:      appRoleAssignment.AppRoleId,
-	})
-	if err != nil {
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	render.Status(r, http.StatusCreated)
-	render.JSON(w, r, g.assignmentToAppRoleAssignment(artur.GetAssignment()))
-}
-
-// DeleteAppRoleAssignment implements the Service interface.
-func (g Graph) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	logger := g.logger.SubloggerWithRequestID(r.Context())
-	logger.Info().Interface("body", r.Body).Msg("calling delete appRoleAssignment")
-
-	userID := chi.URLParam(r, "userID")
-
-	// check assignment belongs to the user
-	lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settingssvc.ListRoleAssignmentsRequest{
-		AccountUuid: userID,
-	})
-	if err != nil {
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	appRoleAssignmentID := chi.URLParam(r, "appRoleAssignmentID")
-
-	assignmentFound := false
-	for _, roleAssignment := range lrar.GetAssignments() {
-		if roleAssignment.Id == appRoleAssignmentID {
-			assignmentFound = true
-		}
-	}
-	if !assignmentFound {
-		errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, fmt.Sprintf("appRoleAssignment %v not found for user %v", appRoleAssignmentID, userID))
-		return
-	}
-
-	_, err = g.roleService.RemoveRoleFromUser(r.Context(), &settingssvc.RemoveRoleFromUserRequest{
-		Id: appRoleAssignmentID,
-	})
-	if err != nil {
-		errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-		return
-	}
-
-	render.NoContent(w, r)
-}
-
-func (g Graph) assignmentToAppRoleAssignment(assignment *settingsmsg.UserRoleAssignment) libregraph.AppRoleAssignment {
-	appRoleAssignment := libregraph.NewAppRoleAssignmentWithDefaults()
-	appRoleAssignment.SetId(assignment.Id)
-	appRoleAssignment.SetAppRoleId(assignment.RoleId)
-	appRoleAssignment.SetPrincipalType(principalTypeUser) // currently always assigned to the user
-	appRoleAssignment.SetResourceId(g.config.Application.ID)
-	appRoleAssignment.SetResourceDisplayName(g.config.Application.DisplayName)
-	appRoleAssignment.SetPrincipalId(assignment.AccountUuid)
-	// appRoleAssignment.SetPrincipalDisplayName() // TODO fetch and cache
-	return *appRoleAssignment
-}
diff --git a/services/graph/pkg/service/v0/instrument.go b/services/graph/pkg/service/v0/instrument.go
index 27491b1d7d9..1c629bab88c 100644
--- a/services/graph/pkg/service/v0/instrument.go
+++ b/services/graph/pkg/service/v0/instrument.go
@@ -24,16 +24,6 @@ func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	i.next.ServeHTTP(w, r)
 }
 
-// ListApplications implements the Service interface.
-func (i instrument) ListApplications(w http.ResponseWriter, r *http.Request) {
-	i.next.ListApplications(w, r)
-}
-
-// GetApplication implements the Service interface.
-func (i instrument) GetApplication(w http.ResponseWriter, r *http.Request) {
-	i.next.GetApplication(w, r)
-}
-
 // GetMe implements the Service interface.
 func (i instrument) GetMe(w http.ResponseWriter, r *http.Request) {
 	i.next.GetMe(w, r)
@@ -69,21 +59,6 @@ func (i instrument) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
 	i.next.ChangeOwnPassword(w, r)
 }
 
-// ListAppRoleAssignments implements the Service interface.
-func (i instrument) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
-	i.next.ListAppRoleAssignments(w, r)
-}
-
-// CreateAppRoleAssignment implements the Service interface.
-func (i instrument) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	i.next.CreateAppRoleAssignment(w, r)
-}
-
-// DeleteAppRoleAssignment implements the Service interface.
-func (i instrument) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	i.next.DeleteAppRoleAssignment(w, r)
-}
-
 // GetGroups implements the Service interface.
 func (i instrument) GetGroups(w http.ResponseWriter, r *http.Request) {
 	i.next.GetGroups(w, r)
diff --git a/services/graph/pkg/service/v0/logging.go b/services/graph/pkg/service/v0/logging.go
index 178071dd1e0..715739b8772 100644
--- a/services/graph/pkg/service/v0/logging.go
+++ b/services/graph/pkg/service/v0/logging.go
@@ -24,16 +24,6 @@ func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	l.next.ServeHTTP(w, r)
 }
 
-// ListApplications implements the Service interface.
-func (l logging) ListApplications(w http.ResponseWriter, r *http.Request) {
-	l.next.ListApplications(w, r)
-}
-
-// GetApplication implements the Service interface.
-func (l logging) GetApplication(w http.ResponseWriter, r *http.Request) {
-	l.next.GetApplication(w, r)
-}
-
 // GetMe implements the Service interface.
 func (l logging) GetMe(w http.ResponseWriter, r *http.Request) {
 	l.next.GetMe(w, r)
@@ -69,21 +59,6 @@ func (l logging) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
 	l.next.ChangeOwnPassword(w, r)
 }
 
-// ListAppRoleAssignments implements the Service interface.
-func (l logging) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
-	l.next.ListAppRoleAssignments(w, r)
-}
-
-// CreateAppRoleAssignment implements the Service interface.
-func (l logging) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	l.next.CreateAppRoleAssignment(w, r)
-}
-
-// DeleteAppRoleAssignment implements the Service interface.
-func (l logging) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	l.next.DeleteAppRoleAssignment(w, r)
-}
-
 // GetGroups implements the Service interface.
 func (l logging) GetGroups(w http.ResponseWriter, r *http.Request) {
 	l.next.GetGroups(w, r)
diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go
index 8ce2d93a243..fedaf6351e8 100644
--- a/services/graph/pkg/service/v0/service.go
+++ b/services/graph/pkg/service/v0/service.go
@@ -32,10 +32,6 @@ const (
 // Service defines the service handlers.
 type Service interface {
 	ServeHTTP(http.ResponseWriter, *http.Request)
-
-	ListApplications(w http.ResponseWriter, r *http.Request)
-	GetApplication(http.ResponseWriter, *http.Request)
-
 	GetMe(http.ResponseWriter, *http.Request)
 	GetUsers(http.ResponseWriter, *http.Request)
 	GetUser(http.ResponseWriter, *http.Request)
@@ -44,10 +40,6 @@ type Service interface {
 	PatchUser(http.ResponseWriter, *http.Request)
 	ChangeOwnPassword(http.ResponseWriter, *http.Request)
 
-	ListAppRoleAssignments(http.ResponseWriter, *http.Request)
-	CreateAppRoleAssignment(http.ResponseWriter, *http.Request)
-	DeleteAppRoleAssignment(http.ResponseWriter, *http.Request)
-
 	GetGroups(http.ResponseWriter, *http.Request)
 	GetGroup(http.ResponseWriter, *http.Request)
 	PostGroup(http.ResponseWriter, *http.Request)
@@ -135,8 +127,80 @@ func NewService(opts ...Option) (Graph, error) {
 		identityEducationBackend: options.IdentityEducationBackend,
 	}
 
-	if err := setIdentityBackends(options, &svc); err != nil {
-		return svc, err
+	if options.IdentityBackend == nil {
+		switch options.Config.Identity.Backend {
+		case "cs3":
+			svc.identityBackend = &identity.CS3{
+				Config: options.Config.Reva,
+				Logger: &options.Logger,
+			}
+		case "ldap":
+			var err error
+
+			var tlsConf *tls.Config
+			if options.Config.Identity.LDAP.Insecure {
+				// When insecure is set to true then we don't need a certificate.
+				options.Config.Identity.LDAP.CACert = ""
+				tlsConf = &tls.Config{
+					MinVersion: tls.VersionTLS12,
+					//nolint:gosec // We need the ability to run with "insecure" (dev/testing)
+					InsecureSkipVerify: options.Config.Identity.LDAP.Insecure,
+				}
+			}
+
+			if options.Config.Identity.LDAP.CACert != "" {
+				if err := ocisldap.WaitForCA(options.Logger,
+					options.Config.Identity.LDAP.Insecure,
+					options.Config.Identity.LDAP.CACert); err != nil {
+					options.Logger.Fatal().Err(err).Msg("The configured LDAP CA cert does not exist")
+				}
+				if tlsConf == nil {
+					tlsConf = &tls.Config{
+						MinVersion: tls.VersionTLS12,
+					}
+				}
+				certs := x509.NewCertPool()
+				pemData, err := os.ReadFile(options.Config.Identity.LDAP.CACert)
+				if err != nil {
+					options.Logger.Error().Err(err).Msgf("Error initializing LDAP Backend")
+					return svc, err
+				}
+				if !certs.AppendCertsFromPEM(pemData) {
+					options.Logger.Error().Msgf("Error initializing LDAP Backend. Adding CA cert failed")
+					return svc, err
+				}
+				tlsConf.RootCAs = certs
+			}
+
+			conn := ldap.NewLDAPWithReconnect(&options.Logger,
+				ldap.Config{
+					URI:          options.Config.Identity.LDAP.URI,
+					BindDN:       options.Config.Identity.LDAP.BindDN,
+					BindPassword: options.Config.Identity.LDAP.BindPassword,
+					TLSConfig:    tlsConf,
+				},
+			)
+			lb, err := identity.NewLDAPBackend(conn, options.Config.Identity.LDAP, &options.Logger)
+			if err != nil {
+				options.Logger.Error().Msgf("Error initializing LDAP Backend: '%s'", err)
+				return svc, err
+			}
+			svc.identityBackend = lb
+			if options.IdentityEducationBackend == nil {
+				if options.Config.Identity.LDAP.EducationResourcesEnabled {
+					svc.identityEducationBackend = lb
+				} else {
+					errEduBackend := &identity.ErrEducationBackend{}
+					svc.identityEducationBackend = errEduBackend
+				}
+			}
+		default:
+			err := fmt.Errorf("Unknown Identity Backend: '%s'", options.Config.Identity.Backend)
+			options.Logger.Err(err)
+			return svc, err
+		}
+	} else {
+		svc.identityBackend = options.IdentityBackend
 	}
 
 	if options.PermissionService == nil {
@@ -145,12 +209,6 @@ func NewService(opts ...Option) (Graph, error) {
 		svc.permissionsService = options.PermissionService
 	}
 
-	if options.RoleService == nil {
-		svc.roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
-	} else {
-		svc.roleService = options.RoleService
-	}
-
 	roleManager := options.RoleManager
 	if roleManager == nil {
 		storeOptions := store.OcisStoreOptions{
@@ -181,10 +239,6 @@ func NewService(opts ...Option) (Graph, error) {
 				r.Put("/tags", svc.AssignTags)
 				r.Delete("/tags", svc.UnassignTags)
 			})
-			r.Route("/applications", func(r chi.Router) {
-				r.Get("/", svc.ListApplications)
-				r.Get("/{applicationID}", svc.GetApplication)
-			})
 			r.Route("/me", func(r chi.Router) {
 				r.Get("/", svc.GetMe)
 				r.Get("/drives", svc.GetDrives)
@@ -198,11 +252,6 @@ func NewService(opts ...Option) (Graph, error) {
 					r.Get("/", svc.GetUser)
 					r.With(requireAdmin).Delete("/", svc.DeleteUser)
 					r.With(requireAdmin).Patch("/", svc.PatchUser)
-					r.With(requireAdmin).Route("/appRoleAssignments", func(r chi.Router) {
-						r.Get("/", svc.ListAppRoleAssignments)
-						r.Post("/", svc.CreateAppRoleAssignment)
-						r.Delete("/{appRoleAssignmentID}", svc.DeleteAppRoleAssignment)
-					})
 				})
 			})
 			r.Route("/groups", func(r chi.Router) {
@@ -273,88 +322,6 @@ func NewService(opts ...Option) (Graph, error) {
 	return svc, nil
 }
 
-func setIdentityBackends(options Options, svc *Graph) error {
-	if options.IdentityBackend == nil {
-		switch options.Config.Identity.Backend {
-		case "cs3":
-			svc.identityBackend = &identity.CS3{
-				Config: options.Config.Reva,
-				Logger: &options.Logger,
-			}
-		case "ldap":
-			var err error
-
-			var tlsConf *tls.Config
-			if options.Config.Identity.LDAP.Insecure {
-
-				// When insecure is set to true then we don't need a certificate.
-				options.Config.Identity.LDAP.CACert = ""
-				tlsConf = &tls.Config{
-					MinVersion: tls.VersionTLS12,
-
-					//nolint:gosec // We need the ability to run with "insecure" (dev/testing)
-					InsecureSkipVerify: options.Config.Identity.LDAP.Insecure,
-				}
-			}
-
-			if options.Config.Identity.LDAP.CACert != "" {
-				if err := ocisldap.WaitForCA(options.Logger,
-					options.Config.Identity.LDAP.Insecure,
-					options.Config.Identity.LDAP.CACert); err != nil {
-					options.Logger.Fatal().Err(err).Msg("The configured LDAP CA cert does not exist")
-				}
-				if tlsConf == nil {
-					tlsConf = &tls.Config{
-						MinVersion: tls.VersionTLS12,
-					}
-				}
-				certs := x509.NewCertPool()
-				pemData, err := os.ReadFile(options.Config.Identity.LDAP.CACert)
-				if err != nil {
-					options.Logger.Error().Err(err).Msg("Error initializing LDAP Backend")
-					return err
-				}
-				if !certs.AppendCertsFromPEM(pemData) {
-					options.Logger.Error().Msg("Error initializing LDAP Backend. Adding CA cert failed")
-					return err
-				}
-				tlsConf.RootCAs = certs
-			}
-
-			conn := ldap.NewLDAPWithReconnect(&options.Logger,
-				ldap.Config{
-					URI:          options.Config.Identity.LDAP.URI,
-					BindDN:       options.Config.Identity.LDAP.BindDN,
-					BindPassword: options.Config.Identity.LDAP.BindPassword,
-					TLSConfig:    tlsConf,
-				},
-			)
-			lb, err := identity.NewLDAPBackend(conn, options.Config.Identity.LDAP, &options.Logger)
-			if err != nil {
-				options.Logger.Error().Err(err).Msg("Error initializing LDAP Backend")
-				return err
-			}
-			svc.identityBackend = lb
-			if options.IdentityEducationBackend == nil {
-				if options.Config.Identity.LDAP.EducationResourcesEnabled {
-					svc.identityEducationBackend = lb
-				} else {
-					errEduBackend := &identity.ErrEducationBackend{}
-					svc.identityEducationBackend = errEduBackend
-				}
-			}
-		default:
-			err := fmt.Errorf("unknown identity backend: '%s'", options.Config.Identity.Backend)
-			options.Logger.Err(err)
-			return err
-		}
-	} else {
-		svc.identityBackend = options.IdentityBackend
-	}
-
-	return nil
-}
-
 // parseHeaderPurge parses the 'Purge' header.
 // '1', 't', 'T', 'TRUE', 'true', 'True' are parsed as true
 // all other values are false.
diff --git a/services/graph/pkg/service/v0/tracing.go b/services/graph/pkg/service/v0/tracing.go
index 54746b94390..691df5f11e7 100644
--- a/services/graph/pkg/service/v0/tracing.go
+++ b/services/graph/pkg/service/v0/tracing.go
@@ -20,16 +20,6 @@ func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	t.next.ServeHTTP(w, r)
 }
 
-// ListApplications implements the Service interface.
-func (t tracing) ListApplications(w http.ResponseWriter, r *http.Request) {
-	t.next.ListApplications(w, r)
-}
-
-// GetApplication implements the Service interface.
-func (t tracing) GetApplication(w http.ResponseWriter, r *http.Request) {
-	t.next.GetApplication(w, r)
-}
-
 // GetMe implements the Service interface.
 func (t tracing) GetMe(w http.ResponseWriter, r *http.Request) {
 	t.next.GetMe(w, r)
@@ -65,21 +55,6 @@ func (t tracing) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
 	t.next.ChangeOwnPassword(w, r)
 }
 
-// ListAppRoleAssignments implements the Service interface.
-func (t tracing) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
-	t.next.ListAppRoleAssignments(w, r)
-}
-
-// CreateAppRoleAssignment implements the Service interface.
-func (t tracing) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	t.next.CreateAppRoleAssignment(w, r)
-}
-
-// DeleteAppRoleAssignment implements the Service interface.
-func (t tracing) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
-	t.next.DeleteAppRoleAssignment(w, r)
-}
-
 // GetGroups implements the Service interface.
 func (t tracing) GetGroups(w http.ResponseWriter, r *http.Request) {
 	t.next.GetGroups(w, r)
diff --git a/services/graph/pkg/service/v0/users.go b/services/graph/pkg/service/v0/users.go
index b37180b12d5..31c390c87e5 100644
--- a/services/graph/pkg/service/v0/users.go
+++ b/services/graph/pkg/service/v0/users.go
@@ -58,30 +58,6 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-
-	// expand appRoleAssignments if requested
-	if slices.Contains(exp, "appRoleAssignments") {
-		lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
-			AccountUuid: me.GetId(),
-		})
-		if err != nil {
-			logger.Debug().Err(err).Str("userid", me.GetId()).Msg("could not get appRoleAssignments for self")
-			var errcode errorcode.Error
-			if errors.As(err, &errcode) {
-				errcode.Render(w, r)
-			} else {
-				errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-			}
-			return
-		}
-
-		values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
-		for _, assignment := range lrar.GetAssignments() {
-			values = append(values, g.assignmentToAppRoleAssignment(assignment))
-		}
-		me.AppRoleAssignments = values
-	}
-
 	render.Status(r, http.StatusOK)
 	render.JSON(w, r, me)
 }
@@ -111,26 +87,6 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	// expand appRoleAssignments if requested
-	exp := strings.Split(r.URL.Query().Get("$expand"), ",")
-	if slices.Contains(exp, "appRoleAssignments") {
-		for _, u := range users {
-			lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
-				AccountUuid: u.GetId(),
-			})
-			if err != nil {
-				logger.Debug().Err(err).Str("userid", u.GetId()).Msg("could not get appRoleAssignments when listing user")
-				continue
-			}
-
-			values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
-			for _, assignment := range lrar.GetAssignments() {
-				values = append(values, g.assignmentToAppRoleAssignment(assignment))
-			}
-			u.AppRoleAssignments = values
-		}
-	}
-
 	users, err = sortUsers(odataReq, users)
 	if err != nil {
 		logger.Debug().Interface("query", odataReq).Msg("error while sorting users according to query")
@@ -318,29 +274,6 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
 			}
 		}
 	}
-	// expand appRoleAssignments if requested
-	if slices.Contains(exp, "appRoleAssignments") {
-
-		lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
-			AccountUuid: user.GetId(),
-		})
-		if err != nil {
-			logger.Debug().Err(err).Str("userid", user.GetId()).Msg("could not get appRoleAssignments for user")
-			var errcode errorcode.Error
-			if errors.As(err, &errcode) {
-				errcode.Render(w, r)
-			} else {
-				errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
-			}
-			return
-		}
-
-		values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
-		for _, assignment := range lrar.GetAssignments() {
-			values = append(values, g.assignmentToAppRoleAssignment(assignment))
-		}
-		user.AppRoleAssignments = values
-	}
 
 	render.Status(r, http.StatusOK)
 	render.JSON(w, r, user)
diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go
index d5eff832243..93998f7c0b2 100644
--- a/services/notifications/pkg/command/server.go
+++ b/services/notifications/pkg/command/server.go
@@ -35,10 +35,8 @@ func Server(cfg *config.Config) *cli.Command {
 			// evs defines a list of events to subscribe to
 			evs := []events.Unmarshaller{
 				events.ShareCreated{},
-				events.ShareExpired{},
 				events.SpaceShared{},
 				events.SpaceUnshared{},
-				events.SpaceMembershipExpired{},
 			}
 
 			evtsCfg := cfg.Notifications.Events
diff --git a/services/notifications/pkg/email/templates/shares/shareExpired.email.body.tmpl b/services/notifications/pkg/email/templates/shares/shareExpired.email.body.tmpl
deleted file mode 100644
index 322f28e520d..00000000000
--- a/services/notifications/pkg/email/templates/shares/shareExpired.email.body.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-Hello {{ .ShareGrantee }},
-
-Your share to {{ .ShareFolder }} has expired at {{ .ExpiredAt }}
-
-Even though this share has been revoked you still might have access through other shares and/or space memberships
-
-----------------------------------------------------------
-
-Hallo {{ .ShareGrantee }},
-
-Deine Freigabe zu {{ .ShareFolder }} ist am {{ .ExpiredAt }} abgelaufen.
-
-Obwohl diese Freigabe nicht mehr zur Verfügung steht, könntest du immer noch Zugriff über andere Freigaben und/oder Space Mitgliedschaften haben.
-
-
----
-ownCloud - Store. Share. Work.
-https://owncloud.com
diff --git a/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl b/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl
deleted file mode 100644
index e9593dcd5af..00000000000
--- a/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl
+++ /dev/null
@@ -1,1 +0,0 @@
-Share to '{{ .ShareFolder }}' expired at {{ .ExpiredAt }}
\ No newline at end of file
diff --git a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.body.tmpl b/services/notifications/pkg/email/templates/spaces/membershipExpired.email.body.tmpl
deleted file mode 100644
index 95ba3897bab..00000000000
--- a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.body.tmpl
+++ /dev/null
@@ -1,18 +0,0 @@
-Hello {{ .SpaceGrantee }},
-
-Your membership of space {{ .SpaceName }} has expired at {{ .ExpiredAt }}
-
-Even though this membership has expired you still might have access through other shares and/or space memberships
-
-----------------------------------------------------------
-
-Hallo {{ .SpaceGrantee }},
-
-Deine Mitgliedschaft zu dem Space {{ .SpaceName }} ist am {{ .ExpiredAt }} abgelaufen.
-
-Obwohl diese Mitgliedschaft nicht mehr zur Verfügung steht, könntest du immer noch Zugriff über andere Freigaben und/oder Space Mitgliedschaften haben.
-
-
----
-ownCloud - Store. Share. Work.
-https://owncloud.com
diff --git a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl b/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl
deleted file mode 100644
index 93658183883..00000000000
--- a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl
+++ /dev/null
@@ -1,1 +0,0 @@
-Membership of '{{ .SpaceName }}' expired at {{ .ExpiredAt }}
\ No newline at end of file
diff --git a/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl b/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl
index c6628dfb3dd..b4a526abde6 100644
--- a/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl
+++ b/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl
@@ -1,1 +1,1 @@
-{{ .SpaceSharer }} removed you from {{ .SpaceName }}
\ No newline at end of file
+{{ .SpaceSharer }} removed you from {{ .SpaceName }}
diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go
index 676f3dfa069..b9a10734ca4 100644
--- a/services/notifications/pkg/service/service.go
+++ b/services/notifications/pkg/service/service.go
@@ -2,7 +2,6 @@ package service
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"net/url"
 	"os"
@@ -11,18 +10,19 @@ import (
 	"syscall"
 
 	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
-	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
-	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
-	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
+	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	revactx "github.com/cs3org/reva/v2/pkg/ctx"
 	"github.com/cs3org/reva/v2/pkg/events"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	"github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
 	"github.com/owncloud/ocis/v2/services/notifications/pkg/email"
+	"google.golang.org/grpc/metadata"
 	"google.golang.org/protobuf/types/known/fieldmaskpb"
 )
 
-// Service should be named `Runner`
 type Service interface {
 	Run() error
 }
@@ -70,12 +70,8 @@ func (s eventsNotifier) Run() error {
 					s.handleSpaceShared(e)
 				case events.SpaceUnshared:
 					s.handleSpaceUnshared(e)
-				case events.SpaceMembershipExpired:
-					s.handleSpaceMembershipExpired(e)
 				case events.ShareCreated:
 					s.handleShareCreated(e)
-				case events.ShareExpired:
-					s.handleShareExpired(e)
 				}
 			}()
 		case <-s.signals:
@@ -86,80 +82,138 @@ func (s eventsNotifier) Run() error {
 	}
 }
 
-func (s eventsNotifier) render(bodyTemplate string, subjTemplate string, values map[string]string) (string, string, error) {
-	msg, err := email.RenderEmailTemplate(bodyTemplate, values, s.emailTemplatePath)
-	if err != nil {
-		return "", "", err
-	}
+func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
+	logger := s.logger.With().
+		Str("event", "ShareCreated").
+		Str("itemid", e.ItemID.OpaqueId).
+		Logger()
 
-	sub, err := email.RenderEmailTemplate(subjTemplate, values, s.emailTemplatePath)
+	impersonateRes, err := s.impersonate(e.Sharer)
 	if err != nil {
-		return "", "", err
+		logger.Error().
+			Err(err).
+			Msg("Could not impersonate sharer")
+		return
 	}
+	ownerCtx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, impersonateRes.Token)
 
-	return msg, sub, nil
-}
-
-func (s eventsNotifier) send(ctx context.Context, u *user.UserId, g *group.GroupId, msg, subj, sender string) error {
-	if u != nil {
-		return s.channel.SendMessage(ctx, []string{u.GetOpaqueId()}, msg, subj, sender)
-
+	resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("could not stat resource")
+		return
 	}
 
-	if g != nil {
-		return s.channel.SendMessageToGroup(ctx, g, msg, subj, sender)
+	shareLink, err := urlJoinPath(s.ocisURL, "files/shares/with-me")
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("could not create link to the share")
+		return
 	}
 
-	return nil
-}
-
-func (s eventsNotifier) getGranteeName(ctx context.Context, u *user.UserId, g *group.GroupId) (string, error) {
+	shareGrantee := ""
 	switch {
-	case u != nil:
-		r, err := s.gwClient.GetUser(ctx, &user.GetUserRequest{UserId: u})
-		if err != nil {
-			return "", err
+	// Note: We're using the 'ownerCtx' (authenticated as the share owner) here for requesting
+	// the Grantees of the shares. Ideally the notfication service would use some kind of service
+	// user for this.
+	case e.GranteeUserID != nil:
+		granteeUserResponse, err := s.gwClient.GetUser(ownerCtx, &userv1beta1.GetUserRequest{
+			UserId: e.GranteeUserID,
+		})
+		if err != nil || granteeUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			s.logger.Error().
+				Err(err).
+				Str("event", "ShareCreated").
+				Msg("Could not get user response from gatway client")
+			return
 		}
-
-		if r.Status.Code != rpc.Code_CODE_OK {
-			return "", fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
+		shareGrantee = granteeUserResponse.GetUser().DisplayName
+	case e.GranteeGroupID != nil:
+		granteeGroupResponse, err := s.gwClient.GetGroup(ownerCtx, &groupv1beta1.GetGroupRequest{
+			GroupId: e.GranteeGroupID,
+		})
+		if err != nil || granteeGroupResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			s.logger.Error().
+				Err(err).
+				Str("event", "ShareCreated").
+				Msg("Could not get group response from gatway client")
+			return
 		}
+		shareGrantee = granteeGroupResponse.GetGroup().DisplayName
+	default:
+		s.logger.Error().
+			Str("event", "ShareCreated").
+			Msg("Event 'ShareCreated' has no grantee")
+		return
+	}
 
-		return r.GetUser().GetDisplayName(), nil
-	case g != nil:
-		r, err := s.gwClient.GetGroup(ctx, &group.GetGroupRequest{GroupId: g})
-		if err != nil {
-			return "", err
-		}
+	sharerDisplayName := impersonateRes.GetUser().DisplayName
+	msg, err := email.RenderEmailTemplate("shares/shareCreated.email.body.tmpl", map[string]string{
+		"ShareGrantee": shareGrantee,
+		"ShareSharer":  sharerDisplayName,
+		"ShareFolder":  resourceInfo.Name,
+		"ShareLink":    shareLink,
+	}, s.emailTemplatePath)
 
-		if r.GetStatus().GetCode() != rpc.Code_CODE_OK {
-			return "", fmt.Errorf("unexpected status code from gateway client: %d", r.GetStatus().GetCode())
-		}
+	if err != nil {
+		s.logger.Error().
+			Err(err).
+			Str("event", "ShareCreated").
+			Msg("Could not render E-Mail body template for shares")
+	}
 
-		return r.GetGroup().GetDisplayName(), nil
-	default:
-		return "", errors.New("Need at least one non-nil grantee")
+	emailSubject, err := email.RenderEmailTemplate("shares/shareCreated.email.subject.tmpl", map[string]string{
+		"ShareSharer": sharerDisplayName,
+		"ShareFolder": resourceInfo.Name,
+	}, s.emailTemplatePath)
+
+	if err != nil {
+		s.logger.Error().
+			Err(err).
+			Str("event", "SpaceCreated").
+			Msg("Could not render E-Mail subject template for shares")
 	}
 
+	if e.GranteeUserID != nil {
+		err = s.channel.SendMessage(ownerCtx, []string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName)
+	} else if e.GranteeGroupID != nil {
+		err = s.channel.SendMessageToGroup(ownerCtx, e.GranteeGroupID, msg, emailSubject, sharerDisplayName)
+	}
+	if err != nil {
+		s.logger.Error().
+			Err(err).
+			Str("event", "ShareCreated").
+			Msg("failed to send a message")
+	}
 }
 
-func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *provider.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*provider.ResourceInfo, error) {
-	// TODO: maybe cache this stat to reduce storage iops
-	md, err := s.gwClient.Stat(ctx, &provider.StatRequest{
-		Ref: &provider.Reference{
-			ResourceId: resourceID,
-		},
-		FieldMask: fieldmask,
+func (s eventsNotifier) impersonate(userID *userv1beta1.UserId) (*gateway.AuthenticateResponse, error) {
+	getUserResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{
+		UserId: userID,
 	})
-
 	if err != nil {
 		return nil, err
 	}
+	if getUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+		return nil, fmt.Errorf("error getting user: %s", getUserResponse.Status.Message)
+	}
 
-	if md.Status.Code != rpc.Code_CODE_OK {
-		return nil, fmt.Errorf("could not resource info: %s", md.Status.Message)
+	// Get auth context
+	ownerCtx := revactx.ContextSetUser(context.Background(), getUserResponse.User)
+	authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
+		Type:         "machine",
+		ClientId:     "userid:" + userID.OpaqueId,
+		ClientSecret: s.machineAuthAPIKey,
+	})
+	if err != nil {
+		return nil, err
 	}
-	return md.GetInfo(), nil
+	if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK {
+		return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message)
+	}
+	return authRes, nil
 }
 
 // TODO: this function is a backport for go1.19 url.JoinPath, upon go bump, replace this
@@ -171,3 +225,22 @@ func urlJoinPath(base string, elements ...string) (string, error) {
 	u.Path = path.Join(append([]string{u.Path}, elements...)...)
 	return u.String(), nil
 }
+
+func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *providerv1beta1.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*providerv1beta1.ResourceInfo, error) {
+	// TODO: maybe cache this stat to reduce storage iops
+	md, err := s.gwClient.Stat(ctx, &providerv1beta1.StatRequest{
+		Ref: &providerv1beta1.Reference{
+			ResourceId: resourceID,
+		},
+		FieldMask: fieldmask,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	if md.Status.Code != rpcv1beta1.Code_CODE_OK {
+		return nil, fmt.Errorf("could not resource info: %s", md.Status.Message)
+	}
+	return md.GetInfo(), nil
+}
diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go
deleted file mode 100644
index 67588494366..00000000000
--- a/services/notifications/pkg/service/shares.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package service
-
-import (
-	"github.com/cs3org/reva/v2/pkg/events"
-	"github.com/cs3org/reva/v2/pkg/utils"
-	"google.golang.org/protobuf/types/known/fieldmaskpb"
-)
-
-func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
-	logger := s.logger.With().
-		Str("event", "ShareCreated").
-		Str("itemid", e.ItemID.OpaqueId).
-		Logger()
-
-	ownerCtx, owner, err := utils.Impersonate(e.Sharer, s.gwClient, s.machineAuthAPIKey)
-	if err != nil {
-		logger.Error().Err(err).Msg("Could not impersonate sharer")
-		return
-	}
-
-	resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
-	if err != nil {
-		logger.Error().
-			Err(err).
-			Msg("could not stat resource")
-		return
-	}
-
-	shareLink, err := urlJoinPath(s.ocisURL, "files/shares/with-me")
-	if err != nil {
-		logger.Error().
-			Err(err).
-			Msg("could not create link to the share")
-		return
-	}
-
-	shareGrantee, err := s.getGranteeName(ownerCtx, e.GranteeUserID, e.GranteeGroupID)
-	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not get grantee name")
-		return
-	}
-
-	sharerDisplayName := owner.GetDisplayName()
-	msg, subj, err := s.render("shares/shareCreated.email.body.tmpl", "shares/shareCreated.email.subject.tmpl", map[string]string{
-		"ShareGrantee": shareGrantee,
-		"ShareSharer":  sharerDisplayName,
-		"ShareFolder":  resourceInfo.Name,
-		"ShareLink":    shareLink,
-	})
-
-	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares")
-	}
-
-	if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message")
-	}
-
-}
-
-func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
-	logger := s.logger.With().
-		Str("event", "ShareExpired").
-		Str("itemid", e.ItemID.GetOpaqueId()).
-		Logger()
-
-	ctx, owner, err := utils.Impersonate(e.ShareOwner, s.gwClient, s.machineAuthAPIKey)
-	if err != nil {
-		logger.Error().Err(err).Msg("Could not impersonate sharer")
-		return
-	}
-
-	resourceInfo, err := s.getResourceInfo(ctx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
-	if err != nil {
-		logger.Error().
-			Err(err).
-			Msg("could not stat resource")
-		return
-	}
-
-	shareGrantee, err := s.getGranteeName(ctx, e.GranteeUserID, e.GranteeGroupID)
-	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not get grantee name")
-		return
-	}
-
-	msg, subj, err := s.render("shares/shareExpired.email.body.tmpl", "shares/shareExpired.email.subject.tmpl", map[string]string{
-		"ShareGrantee": shareGrantee,
-		"ShareFolder":  resourceInfo.GetName(),
-		"ExpiredAt":    e.ExpiredAt.Format("2006-01-02 15:04:05"),
-	})
-
-	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares")
-	}
-
-	if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, msg, subj, owner.GetDisplayName()); err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message")
-	}
-
-}
diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go
index 44affdfcc46..9fd01342b62 100644
--- a/services/notifications/pkg/service/spaces.go
+++ b/services/notifications/pkg/service/spaces.go
@@ -1,9 +1,16 @@
 package service
 
 import (
+	"context"
+
+	groupv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
+	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	revactx "github.com/cs3org/reva/v2/pkg/ctx"
 	"github.com/cs3org/reva/v2/pkg/events"
 	"github.com/cs3org/reva/v2/pkg/storagespace"
-	"github.com/cs3org/reva/v2/pkg/utils"
+	"github.com/owncloud/ocis/v2/services/notifications/pkg/email"
+	"google.golang.org/grpc/metadata"
 )
 
 func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
@@ -12,13 +19,14 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		Str("itemid", e.ID.OpaqueId).
 		Logger()
 
-	ownerCtx, owner, err := utils.Impersonate(e.Executant, s.gwClient, s.machineAuthAPIKey)
+	impersonateRes, err := s.impersonate(e.Executant)
 	if err != nil {
 		logger.Error().
 			Err(err).
 			Msg("could not handle space shared event")
 		return
 	}
+	ownerCtx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, impersonateRes.Token)
 
 	resourceID, err := storagespace.ParseID(e.ID.OpaqueId)
 	if err != nil {
@@ -44,29 +52,73 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		return
 	}
 
+	spaceGrantee := ""
+	switch {
 	// Note: We're using the 'ownerCtx' (authenticated as the share owner) here for requesting
 	// the Grantees of the shares. Ideally the notfication service would use some kind of service
 	// user for this.
-	spaceGrantee, err := s.getGranteeName(ownerCtx, e.GranteeUserID, e.GranteeGroupID)
-	if err != nil {
-		logger.Error().Err(err).Msg("Could not get grantee name")
+	case e.GranteeUserID != nil:
+		granteeUserResponse, err := s.gwClient.GetUser(ownerCtx, &userv1beta1.GetUserRequest{
+			UserId: e.GranteeUserID,
+		})
+		if err != nil || granteeUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			logger.Error().
+				Err(err).
+				Msg("Could not get user response from gatway client")
+			return
+		}
+		spaceGrantee = granteeUserResponse.GetUser().GetDisplayName()
+	case e.GranteeGroupID != nil:
+		granteeGroupResponse, err := s.gwClient.GetGroup(ownerCtx, &groupv1beta1.GetGroupRequest{
+			GroupId: e.GranteeGroupID,
+		})
+		if err != nil || granteeGroupResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			logger.Error().
+				Err(err).
+				Msg("Could not get group response from gatway client")
+			return
+		}
+		spaceGrantee = granteeGroupResponse.GetGroup().GetDisplayName()
+	default:
+		logger.Error().
+			Msg("Event 'SpaceShared' has no grantee")
 		return
 	}
 
-	sharerDisplayName := owner.GetDisplayName()
-	msg, subj, err := s.render("spaces/sharedSpace.email.body.tmpl", "spaces/sharedSpace.email.subject.tmpl", map[string]string{
+	sharerDisplayName := impersonateRes.GetUser().GetDisplayName()
+	msg, err := email.RenderEmailTemplate("spaces/sharedSpace.email.body.tmpl", map[string]string{
 		"SpaceGrantee": spaceGrantee,
 		"SpaceSharer":  sharerDisplayName,
-		"SpaceName":    resourceInfo.GetSpace().GetName(),
+		"SpaceName":    resourceInfo.GetSpace().Name,
 		"ShareLink":    shareLink,
-	})
+	}, s.emailTemplatePath)
+
 	if err != nil {
-		logger.Error().Err(err).Msg("Could not render E-Mail template for spaces")
-		return
+		logger.Error().
+			Err(err).
+			Msg("Could not render E-Mail body template for spaces")
 	}
 
-	if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil {
-		logger.Error().Err(err).Msg("failed to send a message")
+	emailSubject, err := email.RenderEmailTemplate("spaces/sharedSpace.email.subject.tmpl", map[string]string{
+		"SpaceSharer": sharerDisplayName,
+		"SpaceName":   resourceInfo.GetSpace().Name,
+	}, s.emailTemplatePath)
+
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("Could not render E-Mail subject template for spaces")
+	}
+
+	if e.GranteeUserID != nil {
+		err = s.channel.SendMessage(ownerCtx, []string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName)
+	} else if e.GranteeGroupID != nil {
+		err = s.channel.SendMessageToGroup(ownerCtx, e.GranteeGroupID, msg, emailSubject, sharerDisplayName)
+	}
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("failed to send a message")
 	}
 }
 
@@ -76,11 +128,14 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		Str("itemid", e.ID.OpaqueId).
 		Logger()
 
-	ownerCtx, owner, err := utils.Impersonate(e.Executant, s.gwClient, s.machineAuthAPIKey)
+	impersonateRes, err := s.impersonate(e.Executant)
 	if err != nil {
-		logger.Error().Err(err).Msg("could not handle space unshared event")
+		logger.Error().
+			Err(err).
+			Msg("could not handle space unshared event")
 		return
 	}
+	ownerCtx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, impersonateRes.Token)
 
 	resourceID, err := storagespace.ParseID(e.ID.OpaqueId)
 	if err != nil {
@@ -106,63 +161,72 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		return
 	}
 
+	spaceGrantee := ""
+	switch {
 	// Note: We're using the 'ownerCtx' (authenticated as the share owner) here for requesting
 	// the Grantees of the shares. Ideally the notfication service would use some kind of service
 	// user for this.
-	spaceGrantee, err := s.getGranteeName(ownerCtx, e.GranteeUserID, e.GranteeGroupID)
-	if err != nil {
-		logger.Error().Err(err).Msg("Could not get grantee name")
+	case e.GranteeUserID != nil:
+		granteeUserResponse, err := s.gwClient.GetUser(ownerCtx, &userv1beta1.GetUserRequest{
+			UserId: e.GranteeUserID,
+		})
+		if err != nil || granteeUserResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			logger.Error().
+				Err(err).
+				Msg("Could not get user response from gatway client")
+			return
+		}
+		spaceGrantee = granteeUserResponse.GetUser().GetDisplayName()
+	case e.GranteeGroupID != nil:
+		granteeGroupResponse, err := s.gwClient.GetGroup(ownerCtx, &groupv1beta1.GetGroupRequest{
+			GroupId: e.GranteeGroupID,
+		})
+		if err != nil || granteeGroupResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
+			logger.Error().
+				Err(err).
+				Msg("Could not get group response from gatway client")
+			return
+		}
+		spaceGrantee = granteeGroupResponse.GetGroup().GetDisplayName()
+	default:
+		logger.Error().
+			Msg("Event 'SpaceShared' has no grantee")
 		return
 	}
 
-	sharerDisplayName := owner.GetDisplayName()
-	msg, subj, err := s.render("spaces/unsharedSpace.email.body.tmpl", "spaces/unsharedSpace.email.subject.tmpl", map[string]string{
+	sharerDisplayName := impersonateRes.GetUser().GetDisplayName()
+	msg, err := email.RenderEmailTemplate("spaces/unsharedSpace.email.body.tmpl", map[string]string{
 		"SpaceGrantee": spaceGrantee,
 		"SpaceSharer":  sharerDisplayName,
 		"SpaceName":    resourceInfo.GetSpace().Name,
 		"ShareLink":    shareLink,
-	})
+	}, s.emailTemplatePath)
 
 	if err != nil {
-		logger.Error().Err(err).Msg("Could not render E-Mail template for spaces")
-		return
+		logger.Error().
+			Err(err).
+			Msg("Could not render E-Mail body template for spaces")
 	}
 
-	if err := s.send(ownerCtx, e.GranteeUserID, e.GranteeGroupID, msg, subj, sharerDisplayName); err != nil {
-		logger.Error().Err(err).Msg("failed to send a message")
-	}
-}
+	emailSubject, err := email.RenderEmailTemplate("spaces/unsharedSpace.email.subject.tmpl", map[string]string{
+		"SpaceSharer": sharerDisplayName,
+		"SpaceName":   resourceInfo.GetSpace().Name,
+	}, s.emailTemplatePath)
 
-func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExpired) {
-	logger := s.logger.With().
-		Str("event", "SpaceMembershipExpired").
-		Str("itemid", e.SpaceID.GetOpaqueId()).
-		Logger()
-
-	ctx, owner, err := utils.Impersonate(e.SpaceOwner, s.gwClient, s.machineAuthAPIKey)
 	if err != nil {
-		logger.Error().Err(err).Msg("Could not impersonate sharer")
-		return
+		logger.Error().
+			Err(err).
+			Msg("Could not render E-Mail subject template for spaces")
 	}
 
-	shareGrantee, err := s.getGranteeName(ctx, e.GranteeUserID, e.GranteeGroupID)
-	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not get grantee name")
-		return
+	if e.GranteeUserID != nil {
+		err = s.channel.SendMessage(ownerCtx, []string{e.GranteeUserID.OpaqueId}, msg, emailSubject, sharerDisplayName)
+	} else if e.GranteeGroupID != nil {
+		err = s.channel.SendMessageToGroup(ownerCtx, e.GranteeGroupID, msg, emailSubject, sharerDisplayName)
 	}
-
-	msg, subj, err := s.render("spaces/membershipExpired.email.body.tmpl", "spaces/membershipExpired.email.subject.tmpl", map[string]string{
-		"SpaceGrantee": shareGrantee,
-		"SpaceName":    e.SpaceName,
-		"ExpiredAt":    e.ExpiredAt.Format("2006-01-02 15:04:05"),
-	})
-
 	if err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("Could not render E-Mail body template for shares")
-	}
-
-	if err := s.send(ctx, e.GranteeUserID, e.GranteeGroupID, msg, subj, owner.GetDisplayName()); err != nil {
-		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("failed to send a message")
+		logger.Error().
+			Err(err).
+			Msg("failed to send a message")
 	}
-
 }
diff --git a/services/settings/pkg/service/v0/service.go b/services/settings/pkg/service/v0/service.go
index e5db4683d70..0b9955381cd 100644
--- a/services/settings/pkg/service/v0/service.go
+++ b/services/settings/pkg/service/v0/service.go
@@ -79,7 +79,7 @@ func (g Service) CheckPermission(ctx context.Context, req *permissions.CheckPerm
 
 	permission, err := g.manager.ReadPermissionByName(req.Permission, roleIDs)
 	if err != nil {
-		if !errors.Is(err, settings.ErrNotFound) {
+		if !errors.Is(err, settings.ErrPermissionNotFound) {
 			return &permissions.CheckPermissionResponse{
 				Status: status.NewInternal(ctx, err.Error()),
 			}, nil
diff --git a/services/settings/pkg/settings/settings.go b/services/settings/pkg/settings/settings.go
index 6fefd82718e..737585834fc 100644
--- a/services/settings/pkg/settings/settings.go
+++ b/services/settings/pkg/settings/settings.go
@@ -12,12 +12,7 @@ var (
 	Registry = map[string]RegisterFunc{}
 
 	// ErrPermissionNotFound defines a new error for when a permission was not found
-	//
-	// Deprecated use the more generic ErrNotFound
 	ErrPermissionNotFound = errors.New("permission not found")
-
-	// ErrNotFound is the error to use when a resource was not found.
-	ErrNotFound = errors.New("not found")
 )
 
 // RegisterFunc stores store constructors
diff --git a/services/settings/pkg/store/errortypes/errortypes.go b/services/settings/pkg/store/errortypes/errortypes.go
index 0981f1c5cc0..f6c37a644ac 100644
--- a/services/settings/pkg/store/errortypes/errortypes.go
+++ b/services/settings/pkg/store/errortypes/errortypes.go
@@ -1,8 +1,6 @@
 package errortypes
 
 // BundleNotFound is the error to use when a bundle is not found.
-//
-// Deprecated: use the genreric services/settings/pkg/settings.NotFound error
 type BundleNotFound string
 
 func (e BundleNotFound) Error() string { return "error: bundle not found: " + string(e) }
diff --git a/services/settings/pkg/store/filesystem/bundles.go b/services/settings/pkg/store/filesystem/bundles.go
index f0e5749c84a..513bff747f7 100644
--- a/services/settings/pkg/store/filesystem/bundles.go
+++ b/services/settings/pkg/store/filesystem/bundles.go
@@ -2,7 +2,6 @@
 package store
 
 import (
-	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -10,7 +9,7 @@ import (
 
 	"github.com/gofrs/uuid"
 	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
+	"github.com/owncloud/ocis/v2/services/settings/pkg/store/errortypes"
 )
 
 var m = &sync.RWMutex{}
@@ -112,7 +111,7 @@ func (s Store) WriteBundle(record *settingsmsg.Bundle) (*settingsmsg.Bundle, err
 func (s Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting) (*settingsmsg.Setting, error) {
 	bundle, err := s.ReadBundle(bundleID)
 	if err != nil {
-		if !errors.Is(err, settings.ErrNotFound) {
+		if _, notFound := err.(errortypes.BundleNotFound); !notFound {
 			return nil, err
 		}
 		bundle = new(settingsmsg.Bundle)
diff --git a/services/settings/pkg/store/filesystem/io.go b/services/settings/pkg/store/filesystem/io.go
index a1b0f832d3a..5a5cccdaa49 100644
--- a/services/settings/pkg/store/filesystem/io.go
+++ b/services/settings/pkg/store/filesystem/io.go
@@ -1,11 +1,10 @@
 package store
 
 import (
-	"fmt"
 	"io"
 	"os"
 
-	"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
+	"github.com/owncloud/ocis/v2/services/settings/pkg/store/errortypes"
 	"google.golang.org/protobuf/encoding/protojson"
 	"google.golang.org/protobuf/proto"
 )
@@ -14,7 +13,7 @@ import (
 func (s Store) parseRecordFromFile(record proto.Message, filePath string) error {
 	_, err := os.Stat(filePath)
 	if err != nil {
-		return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
+		return errortypes.BundleNotFound(err.Error())
 	}
 
 	file, err := os.Open(filePath)
@@ -29,7 +28,7 @@ func (s Store) parseRecordFromFile(record proto.Message, filePath string) error
 	}
 
 	if len(b) == 0 {
-		return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
+		return errortypes.BundleNotFound(filePath)
 	}
 
 	if err := protojson.Unmarshal(b, record); err != nil {
diff --git a/services/settings/pkg/store/filesystem/permissions.go b/services/settings/pkg/store/filesystem/permissions.go
index 1c03e643871..a01ba654680 100644
--- a/services/settings/pkg/store/filesystem/permissions.go
+++ b/services/settings/pkg/store/filesystem/permissions.go
@@ -55,7 +55,7 @@ func (s Store) ReadPermissionByName(name string, roleIDs []string) (*settingsmsg
 			}
 		}
 	}
-	return nil, settings.ErrNotFound
+	return nil, settings.ErrPermissionNotFound
 }
 
 // extractPermissionsByResource collects all permissions from the provided role that match the requested resource
diff --git a/services/settings/pkg/store/metadata/assignments.go b/services/settings/pkg/store/metadata/assignments.go
index 003b248cf33..36f2adc44c4 100644
--- a/services/settings/pkg/store/metadata/assignments.go
+++ b/services/settings/pkg/store/metadata/assignments.go
@@ -6,10 +6,8 @@ import (
 	"encoding/json"
 	"fmt"
 
-	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/gofrs/uuid"
 	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
 )
 
 // ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
@@ -17,24 +15,14 @@ func (s *Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRole
 	s.Init()
 	ctx := context.TODO()
 	assIDs, err := s.mdc.ReadDir(ctx, accountPath(accountUUID))
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return make([]*settingsmsg.UserRoleAssignment, 0), nil
-	default:
+	if err != nil {
 		return nil, err
 	}
 
 	ass := make([]*settingsmsg.UserRoleAssignment, 0, len(assIDs))
 	for _, assID := range assIDs {
 		b, err := s.mdc.SimpleDownload(ctx, assignmentPath(accountUUID, assID))
-		switch err.(type) {
-		case nil:
-			// continue
-		case errtypes.NotFound:
-			continue
-		default:
+		if err != nil {
 			return nil, err
 		}
 
@@ -54,17 +42,10 @@ func (s *Store) WriteRoleAssignment(accountUUID, roleID string) (*settingsmsg.Us
 	s.Init()
 	ctx := context.TODO()
 	// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
-	err := s.mdc.Delete(ctx, accountPath(accountUUID))
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		// already gone, continue
-	default:
-		return nil, err
-	}
+	_ = s.mdc.Delete(ctx, accountPath(accountUUID))
+	// TODO: How to differentiate between 'not found' and other errors?
 
-	err = s.mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
+	err := s.mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
 	if err != nil {
 		return nil, err
 	}
@@ -86,12 +67,7 @@ func (s *Store) RemoveRoleAssignment(assignmentID string) error {
 	s.Init()
 	ctx := context.TODO()
 	accounts, err := s.mdc.ReadDir(ctx, accountsFolderLocation)
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
-	default:
+	if err != nil {
 		return err
 	}
 
@@ -105,13 +81,11 @@ func (s *Store) RemoveRoleAssignment(assignmentID string) error {
 
 		for _, assID := range assIDs {
 			if assID == assignmentID {
-				// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
-				// we also have to delete the cached dir listing
-				return s.mdc.Delete(ctx, accountPath(accID))
+				return s.mdc.Delete(ctx, assignmentPath(accID, assID))
 			}
 		}
 	}
-	return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
+	return fmt.Errorf("assignmentID '%s' not found", assignmentID)
 }
 
 func accountPath(accountUUID string) string {
diff --git a/services/settings/pkg/store/metadata/bundles.go b/services/settings/pkg/store/metadata/bundles.go
index 9025c97893c..5e25ac53877 100644
--- a/services/settings/pkg/store/metadata/bundles.go
+++ b/services/settings/pkg/store/metadata/bundles.go
@@ -7,10 +7,8 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/gofrs/uuid"
 	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
 )
 
 // ListBundles returns all bundles in the dataPath folder that match the given type.
@@ -20,12 +18,7 @@ func (s *Store) ListBundles(bundleType settingsmsg.Bundle_Type, bundleIDs []stri
 
 	if len(bundleIDs) == 0 {
 		bIDs, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
-		switch err.(type) {
-		case nil:
-			// continue
-		case errtypes.NotFound:
-			return make([]*settingsmsg.Bundle, 0), nil
-		default:
+		if err != nil {
 			return nil, err
 		}
 
@@ -34,12 +27,7 @@ func (s *Store) ListBundles(bundleType settingsmsg.Bundle_Type, bundleIDs []stri
 	var bundles []*settingsmsg.Bundle
 	for _, id := range bundleIDs {
 		b, err := s.mdc.SimpleDownload(ctx, bundlePath(id))
-		switch err.(type) {
-		case nil:
-			// continue
-		case errtypes.NotFound:
-			continue
-		default:
+		if err != nil {
 			return nil, err
 		}
 
@@ -62,12 +50,7 @@ func (s *Store) ReadBundle(bundleID string) (*settingsmsg.Bundle, error) {
 	s.Init()
 	ctx := context.TODO()
 	b, err := s.mdc.SimpleDownload(ctx, bundlePath(bundleID))
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return nil, fmt.Errorf("bundleID '%s' %w", bundleID, settings.ErrNotFound)
-	default:
+	if err != nil {
 		return nil, err
 	}
 
@@ -81,12 +64,7 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
 	ctx := context.TODO()
 
 	ids, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
-	default:
+	if err != nil {
 		return nil, err
 	}
 
@@ -94,9 +72,6 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
 	for _, id := range ids {
 		b, err := s.ReadBundle(id)
 		if err != nil {
-			if errors.Is(err, settings.ErrNotFound) {
-				continue
-			}
 			return nil, err
 		}
 
@@ -107,7 +82,7 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
 		}
 
 	}
-	return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
+	return nil, fmt.Errorf("setting '%s' not found", settingID)
 }
 
 // WriteBundle sends the givens record to the metadataclient. returns `record` for legacy reasons
@@ -127,9 +102,7 @@ func (s *Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting
 	s.Init()
 	b, err := s.ReadBundle(bundleID)
 	if err != nil {
-		if !errors.Is(err, settings.ErrNotFound) {
-			return nil, err
-		}
+		// TODO: How to differentiate 'not found'?
 		b = new(settingsmsg.Bundle)
 		b.Id = bundleID
 		b.Type = settingsmsg.Bundle_TYPE_DEFAULT
diff --git a/services/settings/pkg/store/metadata/permissions.go b/services/settings/pkg/store/metadata/permissions.go
index f13f0bc0992..9756db8d62f 100644
--- a/services/settings/pkg/store/metadata/permissions.go
+++ b/services/settings/pkg/store/metadata/permissions.go
@@ -55,7 +55,7 @@ func (s *Store) ReadPermissionByName(name string, roleIDs []string) (*settingsms
 			}
 		}
 	}
-	return nil, settings.ErrNotFound
+	return nil, settings.ErrPermissionNotFound
 }
 
 // extractPermissionsByResource collects all permissions from the provided role that match the requested resource
diff --git a/services/settings/pkg/store/metadata/values.go b/services/settings/pkg/store/metadata/values.go
index a9df99dacea..5ab790f5076 100644
--- a/services/settings/pkg/store/metadata/values.go
+++ b/services/settings/pkg/store/metadata/values.go
@@ -7,10 +7,8 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/gofrs/uuid"
 	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
 )
 
 // ListValues reads all values that match the given bundleId and accountUUID.
@@ -22,12 +20,7 @@ func (s *Store) ListValues(bundleID, accountUUID string) ([]*settingsmsg.Value,
 	ctx := context.TODO()
 
 	vIDs, err := s.mdc.ReadDir(ctx, valuesFolderLocation)
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return make([]*settingsmsg.Value, 0), nil
-	default:
+	if err != nil {
 		return nil, err
 	}
 
@@ -35,12 +28,7 @@ func (s *Store) ListValues(bundleID, accountUUID string) ([]*settingsmsg.Value,
 	var values []*settingsmsg.Value
 	for _, vid := range vIDs {
 		b, err := s.mdc.SimpleDownload(ctx, valuePath(vid))
-		switch err.(type) {
-		case nil:
-			// continue
-		case errtypes.NotFound:
-			continue
-		default:
+		if err != nil {
 			return nil, err
 		}
 
@@ -73,12 +61,7 @@ func (s *Store) ReadValue(valueID string) (*settingsmsg.Value, error) {
 	ctx := context.TODO()
 
 	b, err := s.mdc.SimpleDownload(ctx, valuePath(valueID))
-	switch err.(type) {
-	case nil:
-		// continue
-	case errtypes.NotFound:
-		return nil, fmt.Errorf("valueID '%s' %w", valueID, settings.ErrNotFound)
-	default:
+	if err != nil {
 		return nil, err
 	}
 	val := &settingsmsg.Value{}


Test patch:
diff --git a/services/graph/pkg/identity/ldap_test.go b/services/graph/pkg/identity/ldap_test.go
index e36466d17e9..3feb5188daa 100644
--- a/services/graph/pkg/identity/ldap_test.go
+++ b/services/graph/pkg/identity/ldap_test.go
@@ -45,8 +45,6 @@ var userEntry = ldap.NewEntry("uid=user",
 		"displayname": {"DisplayName"},
 		"mail":        {"user@example"},
 		"entryuuid":   {"abcd-defg"},
-		"sn":          {"surname"},
-		"givenname":   {"givenName"},
 	})
 
 var invalidUserEntry = ldap.NewEntry("uid=user",
@@ -138,8 +136,6 @@ func TestCreateUser(t *testing.T) {
 	assert.Equal(t, displayName, newUser.GetDisplayName())
 	assert.Equal(t, mail, newUser.GetMail())
 	assert.Equal(t, userName, newUser.GetOnPremisesSamAccountName())
-	assert.Equal(t, givenName, newUser.GetGivenName())
-	assert.Equal(t, surname, newUser.GetSurname())
 }
 
 func TestCreateUserModelFromLDAP(t *testing.T) {
@@ -363,14 +359,14 @@ func TestGetGroup(t *testing.T) {
 		BaseDN:     "uid=user,ou=people,dc=test",
 		SizeLimit:  1,
 		Filter:     "(objectClass=inetOrgPerson)",
-		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname"},
+		Attributes: []string{"displayname", "entryUUID", "mail", "uid"},
 		Controls:   []ldap.Control(nil),
 	}
 	sr3 := &ldap.SearchRequest{
 		BaseDN:     "uid=invalid,ou=people,dc=test",
 		SizeLimit:  1,
 		Filter:     "(objectClass=inetOrgPerson)",
-		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname"},
+		Attributes: []string{"displayname", "entryUUID", "mail", "uid"},
 		Controls:   []ldap.Control(nil),
 	}
 
@@ -458,14 +454,14 @@ func TestGetGroups(t *testing.T) {
 		BaseDN:     "uid=user,ou=people,dc=test",
 		SizeLimit:  1,
 		Filter:     "(objectClass=inetOrgPerson)",
-		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname"},
+		Attributes: []string{"displayname", "entryUUID", "mail", "uid"},
 		Controls:   []ldap.Control(nil),
 	}
 	sr3 := &ldap.SearchRequest{
 		BaseDN:     "uid=invalid,ou=people,dc=test",
 		SizeLimit:  1,
 		Filter:     "(objectClass=inetOrgPerson)",
-		Attributes: []string{"displayname", "entryUUID", "mail", "uid", "sn", "givenname"},
+		Attributes: []string{"displayname", "entryUUID", "mail", "uid"},
 		Controls:   []ldap.Control(nil),
 	}
 
diff --git a/services/graph/pkg/service/v0/application_test.go b/services/graph/pkg/service/v0/application_test.go
deleted file mode 100644
index 028ab59408c..00000000000
--- a/services/graph/pkg/service/v0/application_test.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package svc_test
-
-import (
-	"context"
-	"encoding/json"
-	"io"
-	"net/http"
-	"net/http/httptest"
-
-	"github.com/go-chi/chi/v5"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	libregraph "github.com/owncloud/libre-graph-api-go"
-	"github.com/stretchr/testify/mock"
-
-	ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
-	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
-	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
-	"github.com/owncloud/ocis/v2/services/graph/mocks"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/config"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
-	identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
-	service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
-)
-
-type applicationList struct {
-	Value []*libregraph.Application
-}
-
-var _ = Describe("Applications", func() {
-	var (
-		svc             service.Service
-		ctx             context.Context
-		cfg             *config.Config
-		gatewayClient   *mocks.GatewayClient
-		eventsPublisher mocks.Publisher
-		roleService     *mocks.RoleService
-		identityBackend *identitymocks.Backend
-
-		rr *httptest.ResponseRecorder
-	)
-
-	BeforeEach(func() {
-		eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-
-		identityBackend = &identitymocks.Backend{}
-		roleService = &mocks.RoleService{}
-		gatewayClient = &mocks.GatewayClient{}
-
-		rr = httptest.NewRecorder()
-		ctx = context.Background()
-
-		cfg = defaults.FullDefaultConfig()
-		cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
-		cfg.TokenManager.JWTSecret = "loremipsum"
-		cfg.Commons = &shared.Commons{}
-		cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
-		cfg.Application.ID = "some-application-ID"
-
-		_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
-		svc, _ = service.NewService(
-			service.Config(cfg),
-			service.WithGatewayClient(gatewayClient),
-			service.EventsPublisher(&eventsPublisher),
-			service.WithIdentityBackend(identityBackend),
-			service.WithRoleService(roleService),
-		)
-	})
-
-	Describe("ListApplications", func() {
-		It("lists the configured application with appRoles", func() {
-			roleService.On("ListRoles", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListBundlesResponse{
-				Bundles: []*settingsmsg.Bundle{
-					{
-						Id:          "some-appRole-ID",
-						Type:        settingsmsg.Bundle_TYPE_ROLE,
-						DisplayName: "A human readable name for a role",
-					},
-				},
-			}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/applications", nil)
-			svc.ListApplications(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseList := applicationList{}
-			err = json.Unmarshal(data, &responseList)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(len(responseList.Value)).To(Equal(1))
-			Expect(responseList.Value[0].Id).To(Equal(cfg.Application.ID))
-			Expect(len(responseList.Value[0].GetAppRoles())).To(Equal(1))
-			Expect(responseList.Value[0].GetAppRoles()[0].GetId()).To(Equal("some-appRole-ID"))
-			Expect(responseList.Value[0].GetAppRoles()[0].GetDisplayName()).To(Equal("A human readable name for a role"))
-		})
-	})
-
-	Describe("GetApplication", func() {
-		It("gets the application with appRoles", func() {
-			roleService.On("ListRoles", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListBundlesResponse{
-				Bundles: []*settingsmsg.Bundle{
-					{
-						Id:          "some-appRole-ID",
-						Type:        settingsmsg.Bundle_TYPE_ROLE,
-						DisplayName: "A human readable name for a role",
-					},
-				},
-			}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/applications/some-application-ID", nil)
-			rctx := chi.NewRouteContext()
-			rctx.URLParams.Add("applicationID", cfg.Application.ID)
-			r = r.WithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx))
-			svc.GetApplication(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			application := libregraph.Application{}
-			err = json.Unmarshal(data, &application)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(application.Id).To(Equal(cfg.Application.ID))
-			Expect(len(application.GetAppRoles())).To(Equal(1))
-			Expect(application.GetAppRoles()[0].GetId()).To(Equal("some-appRole-ID"))
-			Expect(application.GetAppRoles()[0].GetDisplayName()).To(Equal("A human readable name for a role"))
-		})
-	})
-
-})
diff --git a/services/graph/pkg/service/v0/approleassignments_test.go b/services/graph/pkg/service/v0/approleassignments_test.go
deleted file mode 100644
index 807bbc1ceda..00000000000
--- a/services/graph/pkg/service/v0/approleassignments_test.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package svc_test
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"io"
-	"net/http"
-	"net/http/httptest"
-
-	userv1beta1 "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/golang/protobuf/ptypes/empty"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	"github.com/stretchr/testify/mock"
-
-	libregraph "github.com/owncloud/libre-graph-api-go"
-	ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
-	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
-	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
-	"github.com/owncloud/ocis/v2/services/graph/mocks"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/config"
-	"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
-	identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
-	service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
-)
-
-type assignmentList struct {
-	Value []*libregraph.AppRoleAssignment
-}
-
-var _ = Describe("AppRoleAssignments", func() {
-	var (
-		svc             service.Service
-		ctx             context.Context
-		cfg             *config.Config
-		gatewayClient   *mocks.GatewayClient
-		eventsPublisher mocks.Publisher
-		roleService     *mocks.RoleService
-		identityBackend *identitymocks.Backend
-
-		rr *httptest.ResponseRecorder
-
-		currentUser = &userv1beta1.User{
-			Id: &userv1beta1.UserId{
-				OpaqueId: "user",
-			},
-		}
-	)
-
-	BeforeEach(func() {
-		eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-
-		identityBackend = &identitymocks.Backend{}
-		roleService = &mocks.RoleService{}
-		gatewayClient = &mocks.GatewayClient{}
-
-		rr = httptest.NewRecorder()
-		ctx = context.Background()
-
-		cfg = defaults.FullDefaultConfig()
-		cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
-		cfg.TokenManager.JWTSecret = "loremipsum"
-		cfg.Commons = &shared.Commons{}
-		cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
-		cfg.Application.ID = "some-application-ID"
-
-		_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
-		svc, _ = service.NewService(
-			service.Config(cfg),
-			service.WithGatewayClient(gatewayClient),
-			service.EventsPublisher(&eventsPublisher),
-			service.WithIdentityBackend(identityBackend),
-			service.WithRoleService(roleService),
-		)
-	})
-
-	Describe("ListAppRoleAssignments", func() {
-		It("lists the appRoleAssignments", func() {
-			user := &libregraph.User{
-				Id: libregraph.PtrString("user1"),
-			}
-			assignments := []*settingsmsg.UserRoleAssignment{
-				{
-					Id:          "some-appRoleAssignment-ID",
-					AccountUuid: user.GetId(),
-					RoleId:      "some-appRole-ID",
-				},
-			}
-			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1/appRoleAssignments", nil)
-			rctx := chi.NewRouteContext()
-			rctx.URLParams.Add("userID", user.GetId())
-			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-			svc.ListAppRoleAssignments(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseList := assignmentList{}
-			err = json.Unmarshal(data, &responseList)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(len(responseList.Value)).To(Equal(1))
-			Expect(responseList.Value[0].GetId()).ToNot(BeEmpty())
-			Expect(responseList.Value[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(responseList.Value[0].GetPrincipalId()).To(Equal(user.GetId()))
-			Expect(responseList.Value[0].GetResourceId()).To(Equal(cfg.Application.ID))
-
-		})
-
-	})
-
-	Describe("CreateAppRoleAssignment", func() {
-		It("creates an appRoleAssignment", func() {
-			user := &libregraph.User{
-				Id: libregraph.PtrString("user1"),
-			}
-			userRoleAssignment := &settingsmsg.UserRoleAssignment{
-				Id:          "some-appRoleAssignment-ID",
-				AccountUuid: user.GetId(),
-				RoleId:      "some-appRole-ID",
-			}
-			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{Assignment: userRoleAssignment}, nil)
-
-			ara := libregraph.NewAppRoleAssignmentWithDefaults()
-			ara.SetAppRoleId("some-appRole-ID")
-			ara.SetPrincipalId(user.GetId())
-			ara.SetResourceId(cfg.Application.ID)
-
-			araJson, err := json.Marshal(ara)
-			Expect(err).ToNot(HaveOccurred())
-
-			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users/user1/appRoleAssignments", bytes.NewBuffer(araJson))
-			rctx := chi.NewRouteContext()
-			rctx.URLParams.Add("userID", user.GetId())
-			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-			svc.CreateAppRoleAssignment(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusCreated))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			assignment := libregraph.AppRoleAssignment{}
-			err = json.Unmarshal(data, &assignment)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(assignment.GetId()).ToNot(BeEmpty())
-			Expect(assignment.GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(assignment.GetPrincipalId()).To(Equal("user1"))
-			Expect(assignment.GetResourceId()).To(Equal(cfg.Application.ID))
-		})
-
-	})
-
-	Describe("DeleteAppRoleAssignment", func() {
-		It("deletes an appRoleAssignment", func() {
-			user := &libregraph.User{
-				Id: libregraph.PtrString("user1"),
-			}
-
-			assignments := []*settingsmsg.UserRoleAssignment{
-				{
-					Id:          "some-appRoleAssignment-ID",
-					AccountUuid: user.GetId(),
-					RoleId:      "some-appRole-ID",
-				},
-			}
-			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-
-			roleService.On("RemoveRoleFromUser", mock.Anything, mock.Anything, mock.Anything).Return(&empty.Empty{}, nil)
-
-			ara := libregraph.NewAppRoleAssignmentWithDefaults()
-			ara.SetAppRoleId("some-appRole-ID")
-			ara.SetPrincipalId(user.GetId())
-			ara.SetResourceId(cfg.Application.ID)
-
-			araJson, err := json.Marshal(ara)
-			Expect(err).ToNot(HaveOccurred())
-
-			r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users/user1/appRoleAssignments/some-appRoleAssignment-ID", bytes.NewBuffer(araJson))
-			rctx := chi.NewRouteContext()
-			rctx.URLParams.Add("userID", user.GetId())
-			rctx.URLParams.Add("appRoleAssignmentID", "some-appRoleAssignment-ID")
-			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-			svc.DeleteAppRoleAssignment(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusNoContent))
-
-		})
-
-	})
-})
diff --git a/services/graph/pkg/service/v0/educationuser_test.go b/services/graph/pkg/service/v0/educationuser_test.go
index 1e9e445cbb9..adc45de6e19 100644
--- a/services/graph/pkg/service/v0/educationuser_test.go
+++ b/services/graph/pkg/service/v0/educationuser_test.go
@@ -74,7 +74,7 @@ var _ = Describe("EducationUsers", func() {
 			service.WithGatewayClient(gatewayClient),
 			service.EventsPublisher(&eventsPublisher),
 			service.WithIdentityEducationBackend(identityEducationBackend),
-			service.WithRoleService(roleService),
+			//service.WithRoleService(roleService),
 		)
 	})
 
diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go
index 77a7f48b0e8..070f547d816 100644
--- a/services/graph/pkg/service/v0/users_test.go
+++ b/services/graph/pkg/service/v0/users_test.go
@@ -17,13 +17,11 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/mock"
-	"go-micro.dev/v4/client"
 
 	libregraph "github.com/owncloud/libre-graph-api-go"
 	ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
 	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
-	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
-	settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
+	settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
 	"github.com/owncloud/ocis/v2/services/graph/mocks"
 	"github.com/owncloud/ocis/v2/services/graph/pkg/config"
 	"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
@@ -69,7 +67,6 @@ var _ = Describe("Users", func() {
 		cfg.TokenManager.JWTSecret = "loremipsum"
 		cfg.Commons = &shared.Commons{}
 		cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
-		cfg.Application.ID = "some-application-ID"
 
 		_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
 		svc, _ = service.NewService(
@@ -97,13 +94,8 @@ var _ = Describe("Users", func() {
 			Expect(rr.Code).To(Equal(http.StatusOK))
 		})
 
-		It("expands the memberOf", func() {
-			user := &libregraph.User{
-				Id: libregraph.PtrString("user1"),
-				MemberOf: []libregraph.Group{
-					{DisplayName: libregraph.PtrString("somegroup")},
-				},
-			}
+		It("expands the user", func() {
+			user := &libregraph.User{}
 			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
 
 			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=memberOf", nil)
@@ -111,48 +103,6 @@ var _ = Describe("Users", func() {
 			svc.GetMe(rr, r)
 
 			Expect(rr.Code).To(Equal(http.StatusOK))
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseUser := &libregraph.User{}
-			err = json.Unmarshal(data, &responseUser)
-			Expect(err).ToNot(HaveOccurred())
-
-			Expect(responseUser.GetId()).To(Equal("user1"))
-			Expect(responseUser.GetMemberOf()).To(HaveLen(1))
-			Expect(responseUser.GetMemberOf()[0].GetDisplayName()).To(Equal("somegroup"))
-
-		})
-
-		It("expands the appRoleAssignments", func() {
-			assignments := []*settingsmsg.UserRoleAssignment{
-				{
-					Id:          "some-appRoleAssignment-ID",
-					AccountUuid: "user",
-					RoleId:      "some-appRole-ID",
-				},
-			}
-			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=appRoleAssignments", nil)
-			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-			svc.GetMe(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseUser := &libregraph.User{}
-			err = json.Unmarshal(data, &responseUser)
-			Expect(err).ToNot(HaveOccurred())
-
-			Expect(responseUser.GetId()).To(Equal("user"))
-			Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
 		})
 	})
 
@@ -266,62 +216,6 @@ var _ = Describe("Users", func() {
 
 			Expect(rr.Code).To(Equal(http.StatusBadRequest))
 		})
-
-		It("expands the appRoleAssignments", func() {
-
-			user := &libregraph.User{}
-			user.SetId("user1")
-			user.SetMail("z@example.com")
-			user.SetDisplayName("9")
-			user.SetOnPremisesSamAccountName("9")
-			user2 := &libregraph.User{}
-			user2.SetId("user2")
-			user2.SetMail("a@example.com")
-			user2.SetDisplayName("1")
-			user2.SetOnPremisesSamAccountName("1")
-			users := []*libregraph.User{user, user2}
-			identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
-
-			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
-				return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
-					{
-						Id:          "some-appRoleAssignment-ID",
-						AccountUuid: in.GetAccountUuid(),
-						RoleId:      "some-appRole-ID",
-					},
-				}}
-			}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=appRoleAssignments", nil)
-			r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
-			svc.GetUsers(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			res := userList{}
-			err = json.Unmarshal(data, &res)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseUsers := res.Value
-			Expect(len(responseUsers)).To(Equal(2))
-			Expect(responseUsers[0].GetId()).To(Equal("user1"))
-			Expect(responseUsers[0].GetAppRoleAssignments()).To(HaveLen(1))
-			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
-			Expect(responseUsers[0].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-
-			Expect(responseUsers[1].GetId()).To(Equal("user2"))
-			Expect(responseUsers[1].GetAppRoleAssignments()).To(HaveLen(1))
-			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user2"))
-			Expect(responseUsers[1].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-
-		})
 	})
 
 	Describe("GetUser", func() {
@@ -428,44 +322,6 @@ var _ = Describe("Users", func() {
 			Expect(responseUser.GetId()).To(Equal("user1"))
 			Expect(len(responseUser.GetDrives())).To(Equal(1))
 		})
-
-		It("expands the appRoleAssignments", func() {
-			user := &libregraph.User{}
-			user.SetId("user1")
-
-			identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
-
-			assignments := []*settingsmsg.UserRoleAssignment{
-				{
-					Id:          "some-appRoleAssignment-ID",
-					AccountUuid: "user1",
-					RoleId:      "some-appRole-ID",
-				},
-			}
-			roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
-
-			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1?$expand=appRoleAssignments", nil)
-			rctx := chi.NewRouteContext()
-			rctx.URLParams.Add("userID", user.GetId())
-			r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
-			svc.GetUser(rr, r)
-
-			Expect(rr.Code).To(Equal(http.StatusOK))
-
-			data, err := io.ReadAll(rr.Body)
-			Expect(err).ToNot(HaveOccurred())
-
-			responseUser := &libregraph.User{}
-			err = json.Unmarshal(data, &responseUser)
-			Expect(err).ToNot(HaveOccurred())
-
-			Expect(responseUser.GetId()).To(Equal("user1"))
-			Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
-			Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
-		})
 	})
 
 	Describe("PostUser", func() {
@@ -525,7 +381,7 @@ var _ = Describe("Users", func() {
 		})
 
 		It("creates a user", func() {
-			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
+			roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settingssvc.AssignRoleToUserResponse{}, nil)
 			identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
 				user.SetId("/users/user")
 				return &user
diff --git a/services/notifications/pkg/service/notification_suite_test.go b/services/notifications/pkg/service/notification_suite_test.go
deleted file mode 100644
index 5735c73ff4c..00000000000
--- a/services/notifications/pkg/service/notification_suite_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package service_test
-
-import (
-	"testing"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-)
-
-func TestNotifications(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "Notification Suite")
-}
diff --git a/services/notifications/pkg/service/service_test.go b/services/notifications/pkg/service/service_test.go
deleted file mode 100644
index 340b2f14922..00000000000
--- a/services/notifications/pkg/service/service_test.go
+++ /dev/null
@@ -1,153 +0,0 @@
-package service_test
-
-import (
-	"context"
-	"time"
-
-	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
-	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
-	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
-	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	"github.com/cs3org/reva/v2/pkg/events"
-	"github.com/cs3org/reva/v2/pkg/utils"
-	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	"github.com/owncloud/ocis/v2/ocis-pkg/log"
-	"github.com/owncloud/ocis/v2/services/notifications/pkg/service"
-	"github.com/test-go/testify/mock"
-)
-
-var _ = Describe("Notifications", func() {
-	var (
-		gwc    *cs3mocks.GatewayAPIClient
-		sharer = &user.User{
-			Id: &user.UserId{
-				OpaqueId: "sharer",
-			},
-			DisplayName: "Dr. S. Harer",
-		}
-		sharee = &user.User{
-			Id: &user.UserId{
-				OpaqueId: "sharee",
-			},
-			DisplayName: "Eric Expireling",
-		}
-		resourceid = &provider.ResourceId{
-			StorageId: "storageid",
-			SpaceId:   "spaceid",
-			OpaqueId:  "itemid",
-		}
-	)
-
-	BeforeEach(func() {
-		gwc = &cs3mocks.GatewayAPIClient{}
-		gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil)
-		gwc.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil)
-		gwc.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, Info: &provider.ResourceInfo{Name: "secrets of the board", Space: &provider.StorageSpace{Name: "secret space"}}}, nil)
-	})
-
-	DescribeTable("Sending notifications",
-		func(tc testChannel, ev interface{}) {
-			ch := make(chan interface{})
-			evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gwc, "", "", "")
-			go evts.Run()
-
-			ch <- ev
-			select {
-			case <-tc.done:
-				// finished
-			case <-time.Tick(3 * time.Second):
-				Fail("timeout waiting for notification")
-			}
-		},
-
-		Entry("Share Created", testChannel{
-			expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
-			expectedSubject:     "Dr. S. Harer shared 'secrets of the board' with you",
-			expectedSender:      sharer.GetDisplayName(),
-			done:                make(chan struct{}),
-		}, events.ShareCreated{
-			Sharer:        sharer.GetId(),
-			GranteeUserID: sharee.GetId(),
-			CTime:         utils.TimeToTS(time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC)),
-			ItemID:        resourceid,
-		}),
-
-		Entry("Share Expired", testChannel{
-			expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
-			expectedSubject:     "Share to 'secrets of the board' expired at 2023-04-17 16:42:00",
-			expectedSender:      sharer.GetDisplayName(),
-			done:                make(chan struct{}),
-		}, events.ShareExpired{
-			ShareOwner:    sharer.GetId(),
-			GranteeUserID: sharee.GetId(),
-			ExpiredAt:     time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC),
-			ItemID:        resourceid,
-		}),
-
-		Entry("Added to Space", testChannel{
-			expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
-			expectedSubject:     "Dr. S. Harer invited you to join secret space",
-			expectedSender:      sharer.GetDisplayName(),
-			done:                make(chan struct{}),
-		}, events.SpaceShared{
-			Executant:     sharer.GetId(),
-			Creator:       sharer.GetId(),
-			GranteeUserID: sharee.GetId(),
-			ID:            &provider.StorageSpaceId{OpaqueId: "spaceid"},
-		}),
-
-		Entry("Removed from Space", testChannel{
-			expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
-			expectedSubject:     "Dr. S. Harer removed you from secret space",
-			expectedSender:      sharer.GetDisplayName(),
-			done:                make(chan struct{}),
-		}, events.SpaceUnshared{
-			Executant:     sharer.GetId(),
-			GranteeUserID: sharee.GetId(),
-			ID:            &provider.StorageSpaceId{OpaqueId: "spaceid"},
-		}),
-
-		Entry("Space Expired", testChannel{
-			expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true},
-			expectedSubject:     "Membership of 'secret space' expired at 2023-04-17 16:42:00",
-			expectedSender:      sharer.GetDisplayName(),
-			done:                make(chan struct{}),
-		}, events.SpaceMembershipExpired{
-			SpaceOwner:    sharer.GetId(),
-			GranteeUserID: sharee.GetId(),
-			SpaceID:       &provider.StorageSpaceId{OpaqueId: "spaceid"},
-			SpaceName:     "secret space",
-			ExpiredAt:     time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC),
-		}),
-	)
-})
-
-// NOTE: This is explictitly not testing the message itself. Should we?
-type testChannel struct {
-	expectedReceipients map[string]bool
-	expectedSubject     string
-	expectedSender      string
-	done                chan struct{}
-}
-
-func (tc testChannel) SendMessage(ctx context.Context, userIDs []string, msg, subject, senderDisplayName string) error {
-	defer GinkgoRecover()
-
-	for _, u := range userIDs {
-		Expect(tc.expectedReceipients[u]).To(Equal(true))
-	}
-
-	// TODO: test the message?
-	//Expect(msg).To(Equal(tc.expectedMessage))
-	Expect(subject).To(Equal(tc.expectedSubject))
-	Expect(senderDisplayName).To(Equal(tc.expectedSender))
-	tc.done <- struct{}{}
-	return nil
-}
-
-func (tc testChannel) SendMessageToGroup(ctx context.Context, groupID *group.GroupId, msg, subject, senderDisplayName string) error {
-	return tc.SendMessage(ctx, []string{groupID.GetOpaqueId()}, msg, subject, senderDisplayName)
-}
diff --git a/services/settings/pkg/store/metadata/store_test.go b/services/settings/pkg/store/metadata/store_test.go
index 0859d38b524..bb1c75ff892 100644
--- a/services/settings/pkg/store/metadata/store_test.go
+++ b/services/settings/pkg/store/metadata/store_test.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"strings"
 
-	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/owncloud/ocis/v2/services/settings/pkg/config/defaults"
 )
 
@@ -54,12 +53,9 @@ func NewMDC(s *Store) error {
 	return s.initMetadataClient(mdc)
 }
 
-// SimpleDownload returns errtypes.NotFound if not found
+// SimpleDownload returns nil if not found
 func (m *MockedMetadataClient) SimpleDownload(_ context.Context, id string) ([]byte, error) {
-	if data, ok := m.data[id]; ok {
-		return data, nil
-	}
-	return nil, errtypes.NotFound("not found")
+	return m.data[id], nil
 }
 
 // SimpleUpload can't error
diff --git a/tests/acceptance/features/coreApiVersions/fileVersions.feature b/tests/acceptance/features/coreApiVersions/fileVersions.feature
index 38a5ef2a7aa..e2f6a0ce6f2 100644
--- a/tests/acceptance/features/coreApiVersions/fileVersions.feature
+++ b/tests/acceptance/features/coreApiVersions/fileVersions.feature
@@ -531,26 +531,3 @@ Feature: dav-versions
     When user "Alice" restores version index "1" of file "/davtest.txt" using the WebDAV API
     Then the HTTP status code should be "204"
     And the content of file "/davtest.txt" for user "Alice" should be "Old Test Content."
-
-  @issue-5010
-  Scenario: Upload the same file twice with the same mtime and a version is available
-    Given 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" uploads file "filesForUpload/textfile.txt" to "file.txt" with mtime "Thu, 08 Aug 2019 04:18:13 GMT" 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 "1" element
-
-  @issue-5010
-  Scenario: Upload the same file more than twice with the same mtime and only one version is available
-    Given 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
-    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" uploads file "filesForUpload/textfile.txt" to "file.txt" with mtime "Thu, 08 Aug 2019 04:18:13 GMT" 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 "1" element
-
-  @issue-5010
-  Scenario: Upload the same file twice with the same mtime and no version after restoring
-    Given 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
-    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