Repository: owncloud/ocis
Pull request #9822 (2024-08-15 15:39:28)

bump libre graph api
bumping the libregraph api ... had some unexpected test failures. please double check the tests.

update-docs[bot]: Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis/blob/master/changelog/README.md) item based on your changes.

phil-davis: @ishabaral @nirajacharya2 please get someone to have a look at the failing API tests.
I think that the updated libre-graph-api is now returning some extra data etc., and test expectations will need to be updated.

saw-jan: >E2e tests are also failing because of that.

webUI actions are failing so I think that means, web also need to accomodate this new change
rhafer: > Looks like `displayName` is now required when:
> 
>     1. changing language
> 
>     2. changing username
> 
>     3. disabling users
> 
> 
> ```
> {"error":{"code":"invalidRequest","innererror":{"date":"2024-08-15T15:44:42Z","request-id":"apiGraphUserGroup/addUserToGroup.feature:281-285"},"message":"invalid request body: no value given for required property displayName"}}
> ```
> 
> E2e tests are also failing because of that.

That shouldn't be the case. I think this is a bug in the generated libregraph code.
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [53.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [53.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [53.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9822&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9822&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [53.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9822&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9822)


saw-jan: @butonic 
you might have to rebase the PR with latest master. These e2e tests should not be running in ocis:
```
1) Scenario: access token renewal via iframe (attempt 2) # tests/e2e/cucumber/features/oidc/iframeTokenRenewal.feature:8
2) Scenario: access token renewal via refresh token (attempt 2) # tests/e2e/cucumber/features/oidc/refreshToken.feature:8
```
recent web bump: https://github.com/owncloud/ocis/pull/9832

Patch:
diff --git a/go.mod b/go.mod
index d47ffb1e595..9e037d7a6df 100644
--- a/go.mod
+++ b/go.mod
@@ -71,7 +71,7 @@ require (
 	github.com/onsi/gomega v1.34.1
 	github.com/open-policy-agent/opa v0.67.1
 	github.com/orcaman/concurrent-map v1.0.0
-	github.com/owncloud/libre-graph-api-go v1.0.5-0.20240618162722-2298241331d1
+	github.com/owncloud/libre-graph-api-go v1.0.5-0.20240816082515-afcf4367b966
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/xattr v0.4.9
 	github.com/prometheus/client_golang v1.19.1
diff --git a/go.sum b/go.sum
index 52899b8c71e..b48e94f022b 100644
--- a/go.sum
+++ b/go.sum
@@ -936,8 +936,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.5-0.20240618162722-2298241331d1 h1:w1OhLyFevK8NCYn50TEsDpInk/T6qVk/v1LDca6Zx8Y=
-github.com/owncloud/libre-graph-api-go v1.0.5-0.20240618162722-2298241331d1/go.mod h1:yXI+rmE8yYx+ZsGVrnCpprw/gZMcxjwntnX2y2+VKxY=
+github.com/owncloud/libre-graph-api-go v1.0.5-0.20240816082515-afcf4367b966 h1:HlbrRDfVqRo/URoIw3AUuPF0/1bIysP5qN1A6Im8djM=
+github.com/owncloud/libre-graph-api-go v1.0.5-0.20240816082515-afcf4367b966/go.mod h1:yXI+rmE8yYx+ZsGVrnCpprw/gZMcxjwntnX2y2+VKxY=
 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/pablodz/inotifywaitgo v0.0.7 h1:1ii49dGBnRn0t1Sz7RGZS6/NberPEDQprwKHN49Bv6U=
diff --git a/ocis-pkg/keycloak/client.go b/ocis-pkg/keycloak/client.go
index 8b191d5dc02..c41381e710e 100644
--- a/ocis-pkg/keycloak/client.go
+++ b/ocis-pkg/keycloak/client.go
@@ -64,7 +64,7 @@ func (c *ConcreteClient) CreateUser(ctx context.Context, realm string, user *lib
 	req := gocloak.User{
 		Email:     user.Mail,
 		Enabled:   user.AccountEnabled,
-		Username:  user.OnPremisesSamAccountName,
+		Username:  &user.OnPremisesSamAccountName,
 		FirstName: user.GivenName,
 		LastName:  user.Surname,
 		Attributes: &map[string][]string{
diff --git a/services/graph/mocks/base_graph_provider.go b/services/graph/mocks/base_graph_provider.go
index 538880cf747..41c9d05645b 100644
--- a/services/graph/mocks/base_graph_provider.go
+++ b/services/graph/mocks/base_graph_provider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/drive_item_permissions_provider.go b/services/graph/mocks/drive_item_permissions_provider.go
index 22dda792299..2bddd682c20 100644
--- a/services/graph/mocks/drive_item_permissions_provider.go
+++ b/services/graph/mocks/drive_item_permissions_provider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/drives_drive_item_provider.go b/services/graph/mocks/drives_drive_item_provider.go
index 77c0a3b767f..9d5093bc301 100644
--- a/services/graph/mocks/drives_drive_item_provider.go
+++ b/services/graph/mocks/drives_drive_item_provider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/gateway_selector.go b/services/graph/mocks/gateway_selector.go
index eedc1f49994..37821769c3f 100644
--- a/services/graph/mocks/gateway_selector.go
+++ b/services/graph/mocks/gateway_selector.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/http_client.go b/services/graph/mocks/http_client.go
index b31a16b269a..4a6750871d8 100644
--- a/services/graph/mocks/http_client.go
+++ b/services/graph/mocks/http_client.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/permissions.go b/services/graph/mocks/permissions.go
index a0645ce951a..f2cd34c891a 100644
--- a/services/graph/mocks/permissions.go
+++ b/services/graph/mocks/permissions.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/publisher.go b/services/graph/mocks/publisher.go
index 7449caf0f6d..8065e1d48fa 100644
--- a/services/graph/mocks/publisher.go
+++ b/services/graph/mocks/publisher.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/mocks/role_service.go b/services/graph/mocks/role_service.go
index d9e19c16e7e..b571e06869d 100644
--- a/services/graph/mocks/role_service.go
+++ b/services/graph/mocks/role_service.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/pkg/identity/backend.go b/services/graph/pkg/identity/backend.go
index 799430128ee..53cbd85efb3 100644
--- a/services/graph/pkg/identity/backend.go
+++ b/services/graph/pkg/identity/backend.go
@@ -32,7 +32,7 @@ type Backend interface {
 	// DeleteUser deletes a given user, identified by username or id, from the backend
 	DeleteUser(ctx context.Context, nameOrID string) error
 	// UpdateUser applies changes to given user, identified by username or id
-	UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error)
+	UpdateUser(ctx context.Context, nameOrID string, user libregraph.UserUpdate) (*libregraph.User, error)
 	GetUser(ctx context.Context, nameOrID string, oreq *godata.GoDataRequest) (*libregraph.User, error)
 	GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libregraph.User, error)
 
@@ -124,9 +124,9 @@ func CreateUserModelFromCS3(u *cs3user.User) *libregraph.User {
 			},
 		},
 		UserType:                 &userType,
-		DisplayName:              &u.DisplayName,
+		DisplayName:              u.DisplayName,
 		Mail:                     &u.Mail,
-		OnPremisesSamAccountName: &u.Username,
+		OnPremisesSamAccountName: u.Username,
 		Id:                       &u.Id.OpaqueId,
 	}
 }
diff --git a/services/graph/pkg/identity/cs3.go b/services/graph/pkg/identity/cs3.go
index 1b1e3eaa05e..cf3c5ac667a 100644
--- a/services/graph/pkg/identity/cs3.go
+++ b/services/graph/pkg/identity/cs3.go
@@ -37,7 +37,7 @@ func (i *CS3) DeleteUser(ctx context.Context, nameOrID string) error {
 }
 
 // UpdateUser implements the Backend Interface. It's currently not supported for the CS3 backend
-func (i *CS3) UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error) {
+func (i *CS3) UpdateUser(ctx context.Context, nameOrID string, user libregraph.UserUpdate) (*libregraph.User, error) {
 	return nil, errNotImplemented
 }
 
diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go
index 37f164d986e..6c73981b3e3 100644
--- a/services/graph/pkg/identity/ldap.go
+++ b/services/graph/pkg/identity/ldap.go
@@ -264,7 +264,7 @@ func (i *LDAP) DeleteUser(ctx context.Context, nameOrID string) error {
 }
 
 // UpdateUser implements the Backend Interface for the LDAP Backend
-func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error) {
+func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.UserUpdate) (*libregraph.User, error) {
 	logger := i.logger.SubloggerWithRequestID(ctx)
 	logger.Debug().Str("backend", "ldap").Msg("UpdateUser")
 	if !i.writeEnabled {
@@ -804,9 +804,9 @@ func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
 
 	if id != "" && opsan != "" {
 		user := &libregraph.User{
-			DisplayName:              pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName)),
+			DisplayName:              e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName),
 			Mail:                     pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.mail)),
-			OnPremisesSamAccountName: &opsan,
+			OnPremisesSamAccountName: opsan,
 			Id:                       &id,
 			GivenName:                pointerOrNil(e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName)),
 			Surname:                  &surname,
@@ -874,7 +874,7 @@ func (i *LDAP) userToLDAPAttrValues(user libregraph.User) (map[string][]string,
 	if user.Surname != nil && *user.Surname != "" {
 		sn = *user.Surname
 	} else {
-		sn = *user.OnPremisesSamAccountName
+		sn = user.OnPremisesSamAccountName
 	}
 	attrs[i.userAttributeMap.surname] = []string{sn}
 
@@ -921,7 +921,7 @@ func (i *LDAP) getUserAttrTypes() []string {
 func (i *LDAP) getUserLDAPDN(user libregraph.User) string {
 	attributeTypeAndValue := ldap.AttributeTypeAndValue{
 		Type:  "uid",
-		Value: *user.OnPremisesSamAccountName,
+		Value: user.OnPremisesSamAccountName,
 	}
 	return fmt.Sprintf("%s,%s", attributeTypeAndValue.String(), i.userBaseDN)
 }
@@ -1256,7 +1256,7 @@ func (i *LDAP) mapLDAPError(err error, errmap ldapResultToErrMap) errorcode.Erro
 	return errorcode.New(errorcode.GeneralException, err.Error())
 }
 
-func isUserEnabledUpdate(user libregraph.User) bool {
+func isUserEnabledUpdate(user libregraph.UserUpdate) bool {
 	switch {
 	case user.Id != nil, user.DisplayName != nil,
 		user.Drive != nil, user.Mail != nil, user.OnPremisesSamAccountName != nil,
diff --git a/services/graph/pkg/identity/ldap_education_user.go b/services/graph/pkg/identity/ldap_education_user.go
index 02814a1c089..2b3b7d9d377 100644
--- a/services/graph/pkg/identity/ldap_education_user.go
+++ b/services/graph/pkg/identity/ldap_education_user.go
@@ -250,12 +250,10 @@ func (i *LDAP) GetEducationUsers(ctx context.Context) ([]*libregraph.EducationUs
 }
 
 func (i *LDAP) educationUserToUser(eduUser libregraph.EducationUser) *libregraph.User {
-	user := libregraph.NewUser()
-	user.OnPremisesSamAccountName = eduUser.OnPremisesSamAccountName
+	user := libregraph.NewUser(*eduUser.DisplayName, *eduUser.OnPremisesSamAccountName)
 	user.Surname = eduUser.Surname
 	user.AccountEnabled = eduUser.AccountEnabled
 	user.GivenName = eduUser.GivenName
-	user.DisplayName = eduUser.DisplayName
 	user.Mail = eduUser.Mail
 	user.UserType = eduUser.UserType
 	user.Identities = eduUser.Identities
@@ -266,11 +264,11 @@ func (i *LDAP) educationUserToUser(eduUser libregraph.EducationUser) *libregraph
 func (i *LDAP) userToEducationUser(user libregraph.User, e *ldap.Entry) *libregraph.EducationUser {
 	eduUser := libregraph.NewEducationUser()
 	eduUser.Id = user.Id
-	eduUser.OnPremisesSamAccountName = user.OnPremisesSamAccountName
+	eduUser.OnPremisesSamAccountName = &user.OnPremisesSamAccountName
 	eduUser.Surname = user.Surname
 	eduUser.AccountEnabled = user.AccountEnabled
 	eduUser.GivenName = user.GivenName
-	eduUser.DisplayName = user.DisplayName
+	eduUser.DisplayName = &user.DisplayName
 	eduUser.Mail = user.Mail
 	eduUser.UserType = user.UserType
 
diff --git a/services/graph/pkg/identity/mocks/backend.go b/services/graph/pkg/identity/mocks/backend.go
index 4a6153290f4..33012ab31be 100644
--- a/services/graph/pkg/identity/mocks/backend.go
+++ b/services/graph/pkg/identity/mocks/backend.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -682,7 +682,7 @@ func (_c *Backend_UpdateGroupName_Call) RunAndReturn(run func(context.Context, s
 }
 
 // UpdateUser provides a mock function with given fields: ctx, nameOrID, user
-func (_m *Backend) UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error) {
+func (_m *Backend) UpdateUser(ctx context.Context, nameOrID string, user libregraph.UserUpdate) (*libregraph.User, error) {
 	ret := _m.Called(ctx, nameOrID, user)
 
 	if len(ret) == 0 {
@@ -691,10 +691,10 @@ func (_m *Backend) UpdateUser(ctx context.Context, nameOrID string, user libregr
 
 	var r0 *libregraph.User
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.User) (*libregraph.User, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.UserUpdate) (*libregraph.User, error)); ok {
 		return rf(ctx, nameOrID, user)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.User) *libregraph.User); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.UserUpdate) *libregraph.User); ok {
 		r0 = rf(ctx, nameOrID, user)
 	} else {
 		if ret.Get(0) != nil {
@@ -702,7 +702,7 @@ func (_m *Backend) UpdateUser(ctx context.Context, nameOrID string, user libregr
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, string, libregraph.User) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, string, libregraph.UserUpdate) error); ok {
 		r1 = rf(ctx, nameOrID, user)
 	} else {
 		r1 = ret.Error(1)
@@ -719,14 +719,14 @@ type Backend_UpdateUser_Call struct {
 // UpdateUser is a helper method to define mock.On call
 //   - ctx context.Context
 //   - nameOrID string
-//   - user libregraph.User
+//   - user libregraph.UserUpdate
 func (_e *Backend_Expecter) UpdateUser(ctx interface{}, nameOrID interface{}, user interface{}) *Backend_UpdateUser_Call {
 	return &Backend_UpdateUser_Call{Call: _e.mock.On("UpdateUser", ctx, nameOrID, user)}
 }
 
-func (_c *Backend_UpdateUser_Call) Run(run func(ctx context.Context, nameOrID string, user libregraph.User)) *Backend_UpdateUser_Call {
+func (_c *Backend_UpdateUser_Call) Run(run func(ctx context.Context, nameOrID string, user libregraph.UserUpdate)) *Backend_UpdateUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(context.Context), args[1].(string), args[2].(libregraph.User))
+		run(args[0].(context.Context), args[1].(string), args[2].(libregraph.UserUpdate))
 	})
 	return _c
 }
@@ -736,7 +736,7 @@ func (_c *Backend_UpdateUser_Call) Return(_a0 *libregraph.User, _a1 error) *Back
 	return _c
 }
 
-func (_c *Backend_UpdateUser_Call) RunAndReturn(run func(context.Context, string, libregraph.User) (*libregraph.User, error)) *Backend_UpdateUser_Call {
+func (_c *Backend_UpdateUser_Call) RunAndReturn(run func(context.Context, string, libregraph.UserUpdate) (*libregraph.User, error)) *Backend_UpdateUser_Call {
 	_c.Call.Return(run)
 	return _c
 }
diff --git a/services/graph/pkg/identity/mocks/education_backend.go b/services/graph/pkg/identity/mocks/education_backend.go
index 8799d04adfe..46587cc9094 100644
--- a/services/graph/pkg/identity/mocks/education_backend.go
+++ b/services/graph/pkg/identity/mocks/education_backend.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/pkg/identity/mocks/ldapclient.go b/services/graph/pkg/identity/mocks/ldapclient.go
index c4dba72e1ed..606c8326078 100644
--- a/services/graph/pkg/identity/mocks/ldapclient.go
+++ b/services/graph/pkg/identity/mocks/ldapclient.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.40.2. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions.go b/services/graph/pkg/service/v0/api_driveitem_permissions.go
index 935a9e094f6..0c571cbec3f 100644
--- a/services/graph/pkg/service/v0/api_driveitem_permissions.go
+++ b/services/graph/pkg/service/v0/api_driveitem_permissions.go
@@ -171,8 +171,9 @@ func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId *sto
 		}
 		permission.GrantedToV2 = &libregraph.SharePointIdentitySet{
 			User: &libregraph.Identity{
-				DisplayName: user.GetDisplayName(),
-				Id:          conversions.ToPointer(user.GetId()),
+				DisplayName:        user.GetDisplayName(),
+				Id:                 conversions.ToPointer(user.GetId()),
+				LibreGraphUserType: conversions.ToPointer(user.GetUserType()),
 			},
 		}
 
diff --git a/services/graph/pkg/service/v0/password.go b/services/graph/pkg/service/v0/password.go
index fa4f08f2b91..8b1c840b1ec 100644
--- a/services/graph/pkg/service/v0/password.go
+++ b/services/graph/pkg/service/v0/password.go
@@ -86,7 +86,7 @@ func (g Graph) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
 
 	newPwProfile := libregraph.NewPasswordProfile()
 	newPwProfile.SetPassword(newPw)
-	changes := libregraph.NewUser()
+	changes := libregraph.NewUserUpdate()
 	changes.SetPasswordProfile(*newPwProfile)
 	_, err = g.identityBackend.UpdateUser(ctx, u.Id.OpaqueId, *changes)
 	if err != nil {
diff --git a/services/graph/pkg/service/v0/users.go b/services/graph/pkg/service/v0/users.go
index 9dfdde5b4b2..0b942a819d1 100644
--- a/services/graph/pkg/service/v0/users.go
+++ b/services/graph/pkg/service/v0/users.go
@@ -339,7 +339,7 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
 func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
 	logger := g.logger.SubloggerWithRequestID(r.Context())
 	logger.Debug().Msg("calling create user")
-	u := libregraph.NewUser()
+	u := libregraph.NewUser("", "")
 	err := StrictJSONUnmarshal(r.Body, u)
 	if err != nil {
 		logger.Info().Err(err).Msg("could not create user: invalid request body")
@@ -347,23 +347,16 @@ func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if _, ok := u.GetDisplayNameOk(); !ok {
-		logger.Info().Err(err).Interface("user", u).Msg("could not create user: missing required Attribute: 'displayName'")
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'displayName'")
+	if u.GetDisplayName() == "" {
+		logger.Info().Err(err).Interface("user", u).Msg("could not create user: empty displayname")
+		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Empty displayname")
 		return
 	}
-	if accountName, ok := u.GetOnPremisesSamAccountNameOk(); ok {
-		if !g.isValidUsername(*accountName) {
-			logger.Info().Str("username", *accountName).Msg("could not create user: invalid username")
-			errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Invalid username")
-			return
-		}
-	} else {
-		logger.Info().Interface("user", u).Msg("could not create user: missing required Attribute: 'onPremisesSamAccountName'")
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'onPremisesSamAccountName'")
+	if username := u.GetOnPremisesSamAccountName(); !g.isValidUsername(username) {
+		logger.Info().Str("username", username).Msg("could not create user: invalid username")
+		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Invalid username")
 		return
 	}
-
 	if mail, ok := u.GetMailOk(); ok {
 		if !isValidEmail(*mail) {
 			logger.Info().Str("mail", *u.Mail).Msg("could not create user: invalid email address")
@@ -715,7 +708,7 @@ func (g Graph) PatchMe(w http.ResponseWriter, r *http.Request) {
 		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing user id")
 		return
 	}
-	changes := libregraph.NewUser()
+	changes := libregraph.NewUserUpdate()
 	err := StrictJSONUnmarshal(r.Body, changes)
 	if err != nil {
 		logger.Debug().Err(err).Interface("body", r.Body).Msg("could not update user: invalid request body")
@@ -747,7 +740,7 @@ func (g Graph) PatchUser(w http.ResponseWriter, r *http.Request) {
 		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping user id failed")
 		return
 	}
-	changes := libregraph.NewUser()
+	changes := libregraph.NewUserUpdate()
 	err = StrictJSONUnmarshal(r.Body, changes)
 	if err != nil {
 		logger.Debug().Err(err).Interface("body", r.Body).Msg("could not update user: invalid request body")
@@ -764,7 +757,7 @@ func (g Graph) PatchUser(w http.ResponseWriter, r *http.Request) {
 	g.patchUser(w, r, nameOrID, changes)
 }
 
-func (g Graph) patchUser(w http.ResponseWriter, r *http.Request, nameOrID string, changes *libregraph.User) {
+func (g Graph) patchUser(w http.ResponseWriter, r *http.Request, nameOrID string, changes *libregraph.UserUpdate) {
 	logger := g.logger.SubloggerWithRequestID(r.Context())
 	logger.Debug().Msg("calling patch user")
 
@@ -875,7 +868,7 @@ func (g Graph) patchUser(w http.ResponseWriter, r *http.Request, nameOrID string
 	}
 
 	if name, ok := changes.GetDisplayNameOk(); ok {
-		addfeature("displayname", *name, oldUserValues.DisplayName)
+		addfeature("displayname", *name, &oldUserValues.DisplayName)
 	}
 
 	if userType, ok := changes.GetUserTypeOk(); ok {
diff --git a/services/invitations/pkg/backends/keycloak/backend.go b/services/invitations/pkg/backends/keycloak/backend.go
index 9454d81ca55..85d98aceaae 100644
--- a/services/invitations/pkg/backends/keycloak/backend.go
+++ b/services/invitations/pkg/backends/keycloak/backend.go
@@ -68,7 +68,7 @@ func (b Backend) CreateUser(ctx context.Context, invitation *invitations.Invitat
 	user := &libregraph.User{
 		Mail:                     &invitation.InvitedUserEmailAddress,
 		AccountEnabled:           boolP(true),
-		OnPremisesSamAccountName: &invitation.InvitedUserEmailAddress,
+		OnPremisesSamAccountName: invitation.InvitedUserEmailAddress,
 		Id:                       stringP(u.String()),
 		UserType:                 stringP(userType),
 	}
diff --git a/services/proxy/pkg/user/backend/cs3.go b/services/proxy/pkg/user/backend/cs3.go
index 0083298a035..a1021bc4b9a 100644
--- a/services/proxy/pkg/user/backend/cs3.go
+++ b/services/proxy/pkg/user/backend/cs3.go
@@ -189,7 +189,7 @@ func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string
 	newUser, err := c.libregraphUserFromClaims(claims)
 	if err != nil {
 		c.logger.Error().Err(err).Interface("claims", claims).Msg("Error creating user from claims")
-		return nil, fmt.Errorf("Error creating user from claims: %w", err)
+		return nil, fmt.Errorf("error creating user from claims: %w", err)
 	}
 
 	req := lgClient.UsersApi.CreateUser(newctx).User(newUser)
@@ -242,11 +242,16 @@ func (c cs3backend) UpdateUserIfNeeded(ctx context.Context, user *cs3.User, clai
 
 	// Check if the user needs to be updated, only updates of "displayName" and "mail" are supported
 	// currently.
+	userupdate := libregraph.NewUserUpdate()
 	switch {
 	case newUser.GetDisplayName() != user.GetDisplayName():
-		fallthrough
+		userupdate.SetDisplayName(newUser.GetDisplayName())
 	case newUser.GetMail() != user.GetMail():
-		return c.updateLibregraphUser(user.GetId().GetOpaqueId(), newUser)
+		userupdate.SetMail(newUser.GetMail())
+	}
+
+	if userupdate.HasDisplayName() || userupdate.HasMail() {
+		return c.updateLibregraphUser(user.GetId().GetOpaqueId(), *userupdate)
 	}
 
 	return nil
@@ -389,7 +394,7 @@ func (c cs3backend) getLibregraphGroup(ctx context.Context, client *libregraph.A
 	return lgGroup, nil
 }
 
-func (c cs3backend) updateLibregraphUser(userid string, user libregraph.User) error {
+func (c cs3backend) updateLibregraphUser(userid string, user libregraph.UserUpdate) error {
 	gatewayClient, err := c.gatewaySelector.Next()
 	if err != nil {
 		c.logger.Error().Err(err).Msg("could not select next gateway client")
@@ -408,7 +413,7 @@ func (c cs3backend) updateLibregraphUser(userid string, user libregraph.User) er
 		return err
 	}
 
-	req := lgClient.UserApi.UpdateUser(newctx, userid).User(user)
+	req := lgClient.UserApi.UpdateUser(newctx, userid).UserUpdate(user)
 
 	_, resp, err := req.Execute()
 	defer resp.Body.Close()
@@ -420,7 +425,7 @@ func (c cs3backend) updateLibregraphUser(userid string, user libregraph.User) er
 	return nil
 }
 
-func (c cs3backend) setupLibregraphClient(ctx context.Context, cs3token string) (*libregraph.APIClient, error) {
+func (c cs3backend) setupLibregraphClient(_ context.Context, cs3token string) (*libregraph.APIClient, error) {
 	// Use micro registry to resolve next graph service endpoint
 	next, err := c.graphSelector.Select("com.owncloud.web.graph")
 	if err != nil {
@@ -469,12 +474,12 @@ func (c cs3backend) libregraphUserFromClaims(claims map[string]interface{}) (lib
 	if dn, ok := claims[c.autoProvisionClaims.DisplayName].(string); ok {
 		user.SetDisplayName(dn)
 	} else {
-		return user, fmt.Errorf("Missing claim '%s' (displayName)", c.autoProvisionClaims.DisplayName)
+		return user, fmt.Errorf("missing claim '%s' (displayName)", c.autoProvisionClaims.DisplayName)
 	}
 	if username, ok := claims[c.autoProvisionClaims.Username].(string); ok {
 		user.SetOnPremisesSamAccountName(username)
 	} else {
-		return user, fmt.Errorf("Missing claim '%s' (username)", c.autoProvisionClaims.Username)
+		return user, fmt.Errorf("missing claim '%s' (username)", c.autoProvisionClaims.Username)
 	}
 	// Email is optional so we don't need an 'else' here
 	if mail, ok := claims[c.autoProvisionClaims.Email].(string); ok {
@@ -494,7 +499,7 @@ func (c cs3backend) libregraphUserFromClaims(claims map[string]interface{}) (lib
 	return user, nil
 }
 
-func (c cs3backend) cs3UserFromLibregraph(ctx context.Context, lu *libregraph.User) cs3.User {
+func (c cs3backend) cs3UserFromLibregraph(_ context.Context, lu *libregraph.User) cs3.User {
 	cs3id := cs3.UserId{
 		Type: cs3.UserType_USER_TYPE_PRIMARY,
 		Idp:  c.oidcISS,
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/README.md b/vendor/github.com/owncloud/libre-graph-api-go/README.md
index c6ceee69464..6e01ebb0905 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/README.md
+++ b/vendor/github.com/owncloud/libre-graph-api-go/README.md
@@ -74,7 +74,7 @@ ctx = context.WithValue(context.Background(), libregraph.ContextOperationServerV
 
 ## Documentation for API Endpoints
 
-All URIs are relative to *https://ocis.ocis-traefik.latest.owncloud.works/graph*
+All URIs are relative to *https://ocis.ocis.rolling.owncloud.works/graph*
 
 Class | Method | HTTP request | Description
 ------------ | ------------- | ------------- | -------------
@@ -141,7 +141,7 @@ Class | Method | HTTP request | Description
 *GroupApi* | [**UpdateGroup**](docs/GroupApi.md#updategroup) | **Patch** /v1.0/groups/{group-id} | Update entity in groups
 *GroupsApi* | [**CreateGroup**](docs/GroupsApi.md#creategroup) | **Post** /v1.0/groups | Add new entity to groups
 *GroupsApi* | [**ListGroups**](docs/GroupsApi.md#listgroups) | **Get** /v1.0/groups | Get entities from groups
-*MeChangepasswordApi* | [**ChangeOwnPassword**](docs/MeChangepasswordApi.md#changeownpassword) | **Post** /v1.0/me/changePassword | Chanage your own password
+*MeChangepasswordApi* | [**ChangeOwnPassword**](docs/MeChangepasswordApi.md#changeownpassword) | **Post** /v1.0/me/changePassword | Change your own password
 *MeDriveApi* | [**GetHome**](docs/MeDriveApi.md#gethome) | **Get** /v1.0/me/drive | Get personal space for user
 *MeDriveApi* | [**ListSharedByMe**](docs/MeDriveApi.md#listsharedbyme) | **Get** /v1beta1/me/drive/sharedByMe | Get a list of driveItem objects shared by the current user.
 *MeDriveApi* | [**ListSharedWithMe**](docs/MeDriveApi.md#listsharedwithme) | **Get** /v1beta1/me/drive/sharedWithMe | Get a list of driveItem objects shared with the owner of a drive.
@@ -244,6 +244,7 @@ Class | Method | HTTP request | Description
  - [UnifiedRoleDefinition](docs/UnifiedRoleDefinition.md)
  - [UnifiedRolePermission](docs/UnifiedRolePermission.md)
  - [User](docs/User.md)
+ - [UserUpdate](docs/UserUpdate.md)
  - [Video](docs/Video.md)
 
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_permissions.go b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_permissions.go
index 97ae2f9b348..0fb2401604b 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_permissions.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_permissions.go
@@ -538,6 +538,20 @@ type ApiListPermissionsRequest struct {
 	ApiService *DrivesPermissionsApiService
 	driveId    string
 	itemId     string
+	filter     *string
+	select_    *[]string
+}
+
+// Filter items by property values. By default all permissions are returned and the avalable sharing roles are limited to normal users. To get a list of sharing roles applicable to federated users use the example $select query and combine it with $filter to omit the list of permissions.
+func (r ApiListPermissionsRequest) Filter(filter string) ApiListPermissionsRequest {
+	r.filter = &filter
+	return r
+}
+
+// Select properties to be returned. By default all properties are returned. Select the roles property to fetch the available sharing roles without resolving all the permissions. Combine this with the $filter parameter to fetch the actions applicable to federated users.
+func (r ApiListPermissionsRequest) Select_(select_ []string) ApiListPermissionsRequest {
+	r.select_ = &select_
+	return r
 }
 
 func (r ApiListPermissionsRequest) Execute() (*CollectionOfPermissionsWithAllowedValues, *http.Response, error) {
@@ -595,6 +609,12 @@ func (a *DrivesPermissionsApiService) ListPermissionsExecute(r ApiListPermission
 	localVarQueryParams := url.Values{}
 	localVarFormParams := url.Values{}
 
+	if r.filter != nil {
+		parameterAddToHeaderOrQuery(localVarQueryParams, "$filter", r.filter, "")
+	}
+	if r.select_ != nil {
+		parameterAddToHeaderOrQuery(localVarQueryParams, "$select", r.select_, "csv")
+	}
 	// to determine the Content-Type header
 	localVarHTTPContentTypes := []string{}
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go
index 46b987c8379..3c3b33a6549 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go
@@ -752,6 +752,20 @@ type ApiListPermissionsSpaceRootRequest struct {
 	ctx        context.Context
 	ApiService *DrivesRootApiService
 	driveId    string
+	filter     *string
+	select_    *[]string
+}
+
+// Filter items by property values. By default all permissions are returned and the avalable sharing roles are limited to normal users. To get a list of sharing roles applicable to federated users use the example $select query and combine it with $filter to omit the list of permissions.
+func (r ApiListPermissionsSpaceRootRequest) Filter(filter string) ApiListPermissionsSpaceRootRequest {
+	r.filter = &filter
+	return r
+}
+
+// Select properties to be returned. By default all properties are returned. Select the roles property to fetch the available sharing roles without resolving all the permissions. Combine this with the $filter parameter to fetch the actions applicable to federated users.
+func (r ApiListPermissionsSpaceRootRequest) Select_(select_ []string) ApiListPermissionsSpaceRootRequest {
+	r.select_ = &select_
+	return r
 }
 
 func (r ApiListPermissionsSpaceRootRequest) Execute() (*CollectionOfPermissionsWithAllowedValues, *http.Response, error) {
@@ -806,6 +820,12 @@ func (a *DrivesRootApiService) ListPermissionsSpaceRootExecute(r ApiListPermissi
 	localVarQueryParams := url.Values{}
 	localVarFormParams := url.Values{}
 
+	if r.filter != nil {
+		parameterAddToHeaderOrQuery(localVarQueryParams, "$filter", r.filter, "")
+	}
+	if r.select_ != nil {
+		parameterAddToHeaderOrQuery(localVarQueryParams, "$select", r.select_, "csv")
+	}
 	// to determine the Content-Type header
 	localVarHTTPContentTypes := []string{}
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_me_changepassword.go b/vendor/github.com/owncloud/libre-graph-api-go/api_me_changepassword.go
index e1577733936..048605c55cc 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/api_me_changepassword.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/api_me_changepassword.go
@@ -38,7 +38,7 @@ func (r ApiChangeOwnPasswordRequest) Execute() (*http.Response, error) {
 }
 
 /*
-ChangeOwnPassword Chanage your own password
+ChangeOwnPassword Change your own password
 
 	@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
 	@return ApiChangeOwnPasswordRequest
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_me_user.go b/vendor/github.com/owncloud/libre-graph-api-go/api_me_user.go
index bff1974111c..06d1b563bbc 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/api_me_user.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/api_me_user.go
@@ -140,12 +140,12 @@ func (a *MeUserApiService) GetOwnUserExecute(r ApiGetOwnUserRequest) (*User, *ht
 type ApiUpdateOwnUserRequest struct {
 	ctx        context.Context
 	ApiService *MeUserApiService
-	user       *User
+	userUpdate *UserUpdate
 }
 
 // New user values
-func (r ApiUpdateOwnUserRequest) User(user User) ApiUpdateOwnUserRequest {
-	r.user = &user
+func (r ApiUpdateOwnUserRequest) UserUpdate(userUpdate UserUpdate) ApiUpdateOwnUserRequest {
+	r.userUpdate = &userUpdate
 	return r
 }
 
@@ -206,7 +206,7 @@ func (a *MeUserApiService) UpdateOwnUserExecute(r ApiUpdateOwnUserRequest) (*Use
 		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
 	}
 	// body params
-	localVarPostBody = r.user
+	localVarPostBody = r.userUpdate
 	req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
 	if err != nil {
 		return localVarReturnValue, nil, err
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_user.go b/vendor/github.com/owncloud/libre-graph-api-go/api_user.go
index 47de33b9b3d..84f698e8d6d 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/api_user.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/api_user.go
@@ -371,12 +371,12 @@ type ApiUpdateUserRequest struct {
 	ctx        context.Context
 	ApiService *UserApiService
 	userId     string
-	user       *User
+	userUpdate *UserUpdate
 }
 
 // New property values
-func (r ApiUpdateUserRequest) User(user User) ApiUpdateUserRequest {
-	r.user = &user
+func (r ApiUpdateUserRequest) UserUpdate(userUpdate UserUpdate) ApiUpdateUserRequest {
+	r.userUpdate = &userUpdate
 	return r
 }
 
@@ -421,8 +421,8 @@ func (a *UserApiService) UpdateUserExecute(r ApiUpdateUserRequest) (*User, *http
 	localVarHeaderParams := make(map[string]string)
 	localVarQueryParams := url.Values{}
 	localVarFormParams := url.Values{}
-	if r.user == nil {
-		return localVarReturnValue, nil, reportError("user is required and must be specified")
+	if r.userUpdate == nil {
+		return localVarReturnValue, nil, reportError("userUpdate is required and must be specified")
 	}
 
 	// to determine the Content-Type header
@@ -443,7 +443,7 @@ func (a *UserApiService) UpdateUserExecute(r ApiUpdateUserRequest) (*User, *http
 		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
 	}
 	// body params
-	localVarPostBody = r.user
+	localVarPostBody = r.userUpdate
 	req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
 	if err != nil {
 		return localVarReturnValue, nil, err
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/configuration.go b/vendor/github.com/owncloud/libre-graph-api-go/configuration.go
index f3ad18074c6..488c8d77c74 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/configuration.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/configuration.go
@@ -96,8 +96,8 @@ func NewConfiguration() *Configuration {
 		Debug:         false,
 		Servers: ServerConfigurations{
 			{
-				URL:         "https://ocis.ocis-traefik.latest.owncloud.works/graph",
-				Description: "ownCloud Infinite Scale Latest",
+				URL:         "https://ocis.ocis.rolling.owncloud.works/graph",
+				Description: "ownCloud Infinite Scale Rolling Release",
 			},
 			{
 				URL:         "https://localhost:9200/graph",
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_drive.go b/vendor/github.com/owncloud/libre-graph-api-go/model_drive.go
index a270f601016..35ee1b273bd 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_drive.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_drive.go
@@ -22,7 +22,7 @@ var _ MappedNullable = &Drive{}
 
 // Drive The drive represents a space on the storage.
 type Drive struct {
-	// The unique idenfier for this drive.
+	// The unique identifier for this drive.
 	Id        *string      `json:"id,omitempty"`
 	CreatedBy *IdentitySet `json:"createdBy,omitempty"`
 	// Date and time of item creation. Read-only.
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go
index e1b71371f3d..0570f044d53 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go
@@ -67,7 +67,7 @@ type DriveItem struct {
 	Video       *Video       `json:"video,omitempty"`
 	// Indicates if the item is synchronized with the underlying storage provider. Read-only.
 	ClientSynchronize *bool `json:"@client.synchronize,omitempty"`
-	// Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissons.
+	// Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissions.
 	UIHidden *bool `json:"@UI.Hidden,omitempty"`
 }
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item_invite.go b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item_invite.go
index df3253030f6..65aefafee8e 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item_invite.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item_invite.go
@@ -20,7 +20,7 @@ var _ MappedNullable = &DriveItemInvite{}
 
 // DriveItemInvite struct for DriveItemInvite
 type DriveItemInvite struct {
-	// A collection of recipients who will receive access and the sharing invitation. Currently, only internal users or gorups are supported.
+	// A collection of recipients who will receive access and the sharing invitation. Currently, only internal users or groups are supported.
 	Recipients []DriveRecipient `json:"recipients,omitempty"`
 	// Specifies the roles that are to be granted to the recipients of the sharing invitation.
 	Roles []string `json:"roles,omitempty"`
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_update.go b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_update.go
index 6007086ca29..7e7a528fd02 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_update.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_update.go
@@ -20,7 +20,7 @@ var _ MappedNullable = &DriveUpdate{}
 
 // DriveUpdate The drive represents an update to a space on the storage.
 type DriveUpdate struct {
-	// The unique idenfier for this drive.
+	// The unique identifier for this drive.
 	Id        *string      `json:"id,omitempty"`
 	CreatedBy *IdentitySet `json:"createdBy,omitempty"`
 	// Date and time of item creation. Read-only.
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_education_school.go b/vendor/github.com/owncloud/libre-graph-api-go/model_education_school.go
index 9601ea86e64..6cfb0f63f1b 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_education_school.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_education_school.go
@@ -20,7 +20,7 @@ var _ MappedNullable = &EducationSchool{}
 
 // EducationSchool Represents a school
 type EducationSchool struct {
-	// The unique idenfier for an entity. Read-only.
+	// The unique identifier for an entity. Read-only.
 	Id *string `json:"id,omitempty"`
 	// The organization name
 	DisplayName *string `json:"displayName,omitempty"`
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_education_user.go b/vendor/github.com/owncloud/libre-graph-api-go/model_education_user.go
index 64c2c4ffa2e..97cce0b6d6b 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_education_user.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_education_user.go
@@ -17,7 +17,7 @@ import (
 // checks if the EducationUser type satisfies the MappedNullable interface at compile time
 var _ MappedNullable = &EducationUser{}
 
-// EducationUser And extension of user with education specific attributes
+// EducationUser An extension of user with education-specific attributes
 type EducationUser struct {
 	// Read-only.
 	Id *string `json:"id,omitempty"`
@@ -43,7 +43,7 @@ type EducationUser struct {
 	GivenName *string `json:"givenName,omitempty"`
 	// The user`s default role. Such as \"student\" or \"teacher\"
 	PrimaryRole *string `json:"primaryRole,omitempty"`
-	// The user`s type. This can be either \"Member\" for regular user, or \"Guest\" for guest users.
+	// The user`s type. This can be either \"Member\" for regular user, \"Guest\" for guest users or \"Federated\" for users imported from a federated instance.
 	UserType *string `json:"userType,omitempty"`
 }
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_geo_coordinates.go b/vendor/github.com/owncloud/libre-graph-api-go/model_geo_coordinates.go
index 8deb1019d51..a39e8d4122e 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_geo_coordinates.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_geo_coordinates.go
@@ -17,7 +17,7 @@ import (
 // checks if the GeoCoordinates type satisfies the MappedNullable interface at compile time
 var _ MappedNullable = &GeoCoordinates{}
 
-// GeoCoordinates The GeoCoordinates resource provides geographic coordinates and elevation of a location based on metadata contained within the file. If a DriveItem has a non-null location facet, the item represents a file with a known location assocaited with it.
+// GeoCoordinates The GeoCoordinates resource provides geographic coordinates and elevation of a location based on metadata contained within the file. If a DriveItem has a non-null location facet, the item represents a file with a known location associated with it.
 type GeoCoordinates struct {
 	// The altitude (height), in feet, above sea level for the item. Read-only.
 	Altitude *float64 `json:"altitude,omitempty"`
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_identity.go b/vendor/github.com/owncloud/libre-graph-api-go/model_identity.go
index 08a5841f82c..7108355b2f2 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_identity.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_identity.go
@@ -25,6 +25,8 @@ type Identity struct {
 	DisplayName string `json:"displayName"`
 	// Unique identifier for the identity.
 	Id *string `json:"id,omitempty"`
+	// The type of the identity. This can be either \"Member\" for regular user, \"Guest\" for guest users or \"Federated\" for users imported from a federated instance. Can be used by clients to indicate the type of user. For more details, clients should look up and cache the user at the /users enpoint.
+	LibreGraphUserType *string `json:"@libre.graph.userType,omitempty"`
 }
 
 type _Identity Identity
@@ -103,6 +105,38 @@ func (o *Identity) SetId(v string) {
 	o.Id = &v
 }
 
+// GetLibreGraphUserType returns the LibreGraphUserType field value if set, zero value otherwise.
+func (o *Identity) GetLibreGraphUserType() string {
+	if o == nil || IsNil(o.LibreGraphUserType) {
+		var ret string
+		return ret
+	}
+	return *o.LibreGraphUserType
+}
+
+// GetLibreGraphUserTypeOk returns a tuple with the LibreGraphUserType field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *Identity) GetLibreGraphUserTypeOk() (*string, bool) {
+	if o == nil || IsNil(o.LibreGraphUserType) {
+		return nil, false
+	}
+	return o.LibreGraphUserType, true
+}
+
+// HasLibreGraphUserType returns a boolean if a field has been set.
+func (o *Identity) HasLibreGraphUserType() bool {
+	if o != nil && !IsNil(o.LibreGraphUserType) {
+		return true
+	}
+
+	return false
+}
+
+// SetLibreGraphUserType gets a reference to the given string and assigns it to the LibreGraphUserType field.
+func (o *Identity) SetLibreGraphUserType(v string) {
+	o.LibreGraphUserType = &v
+}
+
 func (o Identity) MarshalJSON() ([]byte, error) {
 	toSerialize, err := o.ToMap()
 	if err != nil {
@@ -117,6 +151,9 @@ func (o Identity) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.Id) {
 		toSerialize["id"] = o.Id
 	}
+	if !IsNil(o.LibreGraphUserType) {
+		toSerialize["@libre.graph.userType"] = o.LibreGraphUserType
+	}
 	return toSerialize, nil
 }
 
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_user.go b/vendor/github.com/owncloud/libre-graph-api-go/model_user.go
index 4a6262a73fe..995728311f4 100644
--- a/vendor/github.com/owncloud/libre-graph-api-go/model_user.go
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_user.go
@@ -11,7 +11,9 @@ API version: v1.0.4
 package libregraph
 
 import (
+	"bytes"
 	"encoding/json"
+	"fmt"
 )
 
 // checks if the User type satisfies the MappedNullable interface at compile time
@@ -26,7 +28,7 @@ type User struct {
 	// The apps and app roles which this user has been assigned.
 	AppRoleAssignments []AppRoleAssignment `json:"appRoleAssignments,omitempty"`
 	// The name displayed in the address book for the user. This value is usually the combination of the user's first name, middle initial, and last name. This property is required when a user is created and it cannot be cleared during updates. Returned by default. Supports $orderby.
-	DisplayName *string `json:"displayName,omitempty"`
+	DisplayName string `json:"displayName"`
 	// A collection of drives available for this user. Read-only.
 	Drives []Drive `json:"drives,omitempty"`
 	Drive  *Drive  `json:"drive,omitempty"`
@@ -36,25 +38,29 @@ type User struct {
 	Mail *string `json:"mail,omitempty"`
 	// Groups that this user is a member of. HTTP Methods: GET (supported for all groups). Read-only. Nullable. Supports $expand.
 	MemberOf []Group `json:"memberOf,omitempty"`
-	// Contains the on-premises SAM account name synchronized from the on-premises directory. Read-only.
-	OnPremisesSamAccountName *string          `json:"onPremisesSamAccountName,omitempty"`
+	// Contains the on-premises SAM account name synchronized from the on-premises directory.
+	OnPremisesSamAccountName string           `json:"onPremisesSamAccountName"`
 	PasswordProfile          *PasswordProfile `json:"passwordProfile,omitempty"`
 	// The user's surname (family name or last name). Returned by default.
 	Surname *string `json:"surname,omitempty"`
 	// The user's givenName. Returned by default.
 	GivenName *string `json:"givenName,omitempty"`
-	// The user`s type. This can be either \"Member\" for regular user, or \"Guest\" for guest users.
+	// The user`s type. This can be either \"Member\" for regular user, \"Guest\" for guest users or \"Federated\" for users imported from a federated instance.
 	UserType *string `json:"userType,omitempty"`
 	// Represents the users language setting, ISO-639-1 Code
 	PreferredLanguage *string `json:"preferredLanguage,omitempty"`
 }
 
+type _User User
+
 // NewUser instantiates a new User object
 // This constructor will assign default values to properties that have it defined,
 // and makes sure properties required by API are set, but the set of arguments
 // will change when the set of required properties is changed
-func NewUser() *User {
+func NewUser(displayName string, onPremisesSamAccountName string) *User {
 	this := User{}
+	this.DisplayName = displayName
+	this.OnPremisesSamAccountName = onPremisesSamAccountName
 	return &this
 }
 
@@ -162,36 +168,28 @@ func (o *User) SetAppRoleAssignments(v []AppRoleAssignment) {
 	o.AppRoleAssignments = v
 }
 
-// GetDisplayName returns the DisplayName field value if set, zero value otherwise.
+// GetDisplayName returns the DisplayName field value
 func (o *User) GetDisplayName() string {
-	if o == nil || IsNil(o.DisplayName) {
+	if o == nil {
 		var ret string
 		return ret
 	}
-	return *o.DisplayName
+
+	return o.DisplayName
 }
 
-// GetDisplayNameOk returns a tuple with the DisplayName field value if set, nil otherwise
+// GetDisplayNameOk returns a tuple with the DisplayName field value
 // and a boolean to check if the value has been set.
 func (o *User) GetDisplayNameOk() (*string, bool) {
-	if o == nil || IsNil(o.DisplayName) {
+	if o == nil {
 		return nil, false
 	}
-	return o.DisplayName, true
+	return &o.DisplayName, true
 }
 
-// HasDisplayName returns a boolean if a field has been set.
-func (o *User) HasDisplayName() bool {
-	if o != nil && !IsNil(o.DisplayName) {
-		return true
-	}
-
-	return false
-}
-
-// SetDisplayName gets a reference to the given string and assigns it to the DisplayName field.
+// SetDisplayName sets field value
 func (o *User) SetDisplayName(v string) {
-	o.DisplayName = &v
+	o.DisplayName = v
 }
 
 // GetDrives returns the Drives field value if set, zero value otherwise.
@@ -354,36 +352,28 @@ func (o *User) SetMemberOf(v []Group) {
 	o.MemberOf = v
 }
 
-// GetOnPremisesSamAccountName returns the OnPremisesSamAccountName field value if set, zero value otherwise.
+// GetOnPremisesSamAccountName returns the OnPremisesSamAccountName field value
 func (o *User) GetOnPremisesSamAccountName() string {
-	if o == nil || IsNil(o.OnPremisesSamAccountName) {
+	if o == nil {
 		var ret string
 		return ret
 	}
-	return *o.OnPremisesSamAccountName
+
+	return o.OnPremisesSamAccountName
 }
 
-// GetOnPremisesSamAccountNameOk returns a tuple with the OnPremisesSamAccountName field value if set, nil otherwise
+// GetOnPremisesSamAccountNameOk returns a tuple with the OnPremisesSamAccountName field value
 // and a boolean to check if the value has been set.
 func (o *User) GetOnPremisesSamAccountNameOk() (*string, bool) {
-	if o == nil || IsNil(o.OnPremisesSamAccountName) {
+	if o == nil {
 		return nil, false
 	}
-	return o.OnPremisesSamAccountName, true
+	return &o.OnPremisesSamAccountName, true
 }
 
-// HasOnPremisesSamAccountName returns a boolean if a field has been set.
-func (o *User) HasOnPremisesSamAccountName() bool {
-	if o != nil && !IsNil(o.OnPremisesSamAccountName) {
-		return true
-	}
-
-	return false
-}
-
-// SetOnPremisesSamAccountName gets a reference to the given string and assigns it to the OnPremisesSamAccountName field.
+// SetOnPremisesSamAccountName sets field value
 func (o *User) SetOnPremisesSamAccountName(v string) {
-	o.OnPremisesSamAccountName = &v
+	o.OnPremisesSamAccountName = v
 }
 
 // GetPasswordProfile returns the PasswordProfile field value if set, zero value otherwise.
@@ -565,9 +555,7 @@ func (o User) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.AppRoleAssignments) {
 		toSerialize["appRoleAssignments"] = o.AppRoleAssignments
 	}
-	if !IsNil(o.DisplayName) {
-		toSerialize["displayName"] = o.DisplayName
-	}
+	toSerialize["displayName"] = o.DisplayName
 	if !IsNil(o.Drives) {
 		toSerialize["drives"] = o.Drives
 	}
@@ -583,9 +571,7 @@ func (o User) ToMap() (map[string]interface{}, error) {
 	if !IsNil(o.MemberOf) {
 		toSerialize["memberOf"] = o.MemberOf
 	}
-	if !IsNil(o.OnPremisesSamAccountName) {
-		toSerialize["onPremisesSamAccountName"] = o.OnPremisesSamAccountName
-	}
+	toSerialize["onPremisesSamAccountName"] = o.OnPremisesSamAccountName
 	if !IsNil(o.PasswordProfile) {
 		toSerialize["passwordProfile"] = o.PasswordProfile
 	}
@@ -604,6 +590,44 @@ func (o User) ToMap() (map[string]interface{}, error) {
 	return toSerialize, nil
 }
 
+func (o *User) UnmarshalJSON(data []byte) (err error) {
+	// This validates that all required properties are included in the JSON object
+	// by unmarshalling the object into a generic map with string keys and checking
+	// that every required field exists as a key in the generic map.
+	requiredProperties := []string{
+		"displayName",
+		"onPremisesSamAccountName",
+	}
+
+	allProperties := make(map[string]interface{})
+
+	err = json.Unmarshal(data, &allProperties)
+
+	if err != nil {
+		return err
+	}
+
+	for _, requiredProperty := range requiredProperties {
+		if _, exists := allProperties[requiredProperty]; !exists {
+			return fmt.Errorf("no value given for required property %v", requiredProperty)
+		}
+	}
+
+	varUser := _User{}
+
+	decoder := json.NewDecoder(bytes.NewReader(data))
+	decoder.DisallowUnknownFields()
+	err = decoder.Decode(&varUser)
+
+	if err != nil {
+		return err
+	}
+
+	*o = User(varUser)
+
+	return err
+}
+
 type NullableUser struct {
 	value *User
 	isSet bool
diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_user_update.go b/vendor/github.com/owncloud/libre-graph-api-go/model_user_update.go
new file mode 100644
index 00000000000..9afdf3f0a05
--- /dev/null
+++ b/vendor/github.com/owncloud/libre-graph-api-go/model_user_update.go
@@ -0,0 +1,641 @@
+/*
+Libre Graph API
+
+Libre Graph is a free API for cloud collaboration inspired by the MS Graph API.
+
+API version: v1.0.4
+*/
+
+// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
+
+package libregraph
+
+import (
+	"encoding/json"
+)
+
+// checks if the UserUpdate type satisfies the MappedNullable interface at compile time
+var _ MappedNullable = &UserUpdate{}
+
+// UserUpdate Represents updates to an Active Directory user object.
+type UserUpdate struct {
+	// Read-only.
+	Id *string `json:"id,omitempty"`
+	// Set to \"true\" when the account is enabled.
+	AccountEnabled *bool `json:"accountEnabled,omitempty"`
+	// The apps and app roles which this user has been assigned.
+	AppRoleAssignments []AppRoleAssignment `json:"appRoleAssignments,omitempty"`
+	// The name displayed in the address book for the user. This value is usually the combination of the user's first name, middle initial, and last name. This property is required when a user is created and it cannot be cleared during updates. Returned by default. Supports $orderby.
+	DisplayName *string `json:"displayName,omitempty"`
+	// A collection of drives available for this user. Read-only.
+	Drives []Drive `json:"drives,omitempty"`
+	Drive  *Drive  `json:"drive,omitempty"`
+	// Identities associated with this account.
+	Identities []ObjectIdentity `json:"identities,omitempty"`
+	// The SMTP address for the user, for example, 'jeff@contoso.onowncloud.com'. Returned by default.
+	Mail *string `json:"mail,omitempty"`
+	// Groups that this user is a member of. HTTP Methods: GET (supported for all groups). Read-only. Nullable. Supports $expand.
+	MemberOf []Group `json:"memberOf,omitempty"`
+	// Contains the on-premises SAM account name synchronized from the on-premises directory.
+	OnPremisesSamAccountName *string          `json:"onPremisesSamAccountName,omitempty"`
+	PasswordProfile          *PasswordProfile `json:"passwordProfile,omitempty"`
+	// The user's surname (family name or last name). Returned by default.
+	Surname *string `json:"surname,omitempty"`
+	// The user's givenName. Returned by default.
+	GivenName *string `json:"givenName,omitempty"`
+	// The user`s type. This can be either \"Member\" for regular user, \"Guest\" for guest users or \"Federated\" for users imported from a federated instance.
+	UserType *string `json:"userType,omitempty"`
+	// Represents the users language setting, ISO-639-1 Code
+	PreferredLanguage *string `json:"preferredLanguage,omitempty"`
+}
+
+// NewUserUpdate instantiates a new UserUpdate object
+// This constructor will assign default values to properties that have it defined,
+// and makes sure properties required by API are set, but the set of arguments
+// will change when the set of required properties is changed
+func NewUserUpdate() *UserUpdate {
+	this := UserUpdate{}
+	return &this
+}
+
+// NewUserUpdateWithDefaults instantiates a new UserUpdate object
+// This constructor will only assign default values to properties that have it defined,
+// but it doesn't guarantee that properties required by API are set
+func NewUserUpdateWithDefaults() *UserUpdate {
+	this := UserUpdate{}
+	return &this
+}
+
+// GetId returns the Id field value if set, zero value otherwise.
+func (o *UserUpdate) GetId() string {
+	if o == nil || IsNil(o.Id) {
+		var ret string
+		return ret
+	}
+	return *o.Id
+}
+
+// GetIdOk returns a tuple with the Id field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetIdOk() (*string, bool) {
+	if o == nil || IsNil(o.Id) {
+		return nil, false
+	}
+	return o.Id, true
+}
+
+// HasId returns a boolean if a field has been set.
+func (o *UserUpdate) HasId() bool {
+	if o != nil && !IsNil(o.Id) {
+		return true
+	}
+
+	return false
+}
+
+// SetId gets a reference to the given string and assigns it to the Id field.
+func (o *UserUpdate) SetId(v string) {
+	o.Id = &v
+}
+
+// GetAccountEnabled returns the AccountEnabled field value if set, zero value otherwise.
+func (o *UserUpdate) GetAccountEnabled() bool {
+	if o == nil || IsNil(o.AccountEnabled) {
+		var ret bool
+		return ret
+	}
+	return *o.AccountEnabled
+}
+
+// GetAccountEnabledOk returns a tuple with the AccountEnabled field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetAccountEnabledOk() (*bool, bool) {
+	if o == nil || IsNil(o.AccountEnabled) {
+		return nil, false
+	}
+	return o.AccountEnabled, true
+}
+
+// HasAccountEnabled returns a boolean if a field has been set.
+func (o *UserUpdate) HasAccountEnabled() bool {
+	if o != nil && !IsNil(o.AccountEnabled) {
+		return true
+	}
+
+	return false
+}
+
+// SetAccountEnabled gets a reference to the given bool and assigns it to the AccountEnabled field.
+func (o *UserUpdate) SetAccountEnabled(v bool) {
+	o.AccountEnabled = &v
+}
+
+// GetAppRoleAssignments returns the AppRoleAssignments field value if set, zero value otherwise.
+func (o *UserUpdate) GetAppRoleAssignments() []AppRoleAssignment {
+	if o == nil || IsNil(o.AppRoleAssignments) {
+		var ret []AppRoleAssignment
+		return ret
+	}
+	return o.AppRoleAssignments
+}
+
+// GetAppRoleAssignmentsOk returns a tuple with the AppRoleAssignments field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetAppRoleAssignmentsOk() ([]AppRoleAssignment, bool) {
+	if o == nil || IsNil(o.AppRoleAssignments) {
+		return nil, false
+	}
+	return o.AppRoleAssignments, true
+}
+
+// HasAppRoleAssignments returns a boolean if a field has been set.
+func (o *UserUpdate) HasAppRoleAssignments() bool {
+	if o != nil && !IsNil(o.AppRoleAssignments) {
+		return true
+	}
+
+	return false
+}
+
+// SetAppRoleAssignments gets a reference to the given []AppRoleAssignment and assigns it to the AppRoleAssignments field.
+func (o *UserUpdate) SetAppRoleAssignments(v []AppRoleAssignment) {
+	o.AppRoleAssignments = v
+}
+
+// GetDisplayName returns the DisplayName field value if set, zero value otherwise.
+func (o *UserUpdate) GetDisplayName() string {
+	if o == nil || IsNil(o.DisplayName) {
+		var ret string
+		return ret
+	}
+	return *o.DisplayName
+}
+
+// GetDisplayNameOk returns a tuple with the DisplayName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetDisplayNameOk() (*string, bool) {
+	if o == nil || IsNil(o.DisplayName) {
+		return nil, false
+	}
+	return o.DisplayName, true
+}
+
+// HasDisplayName returns a boolean if a field has been set.
+func (o *UserUpdate) HasDisplayName() bool {
+	if o != nil && !IsNil(o.DisplayName) {
+		return true
+	}
+
+	return false
+}
+
+// SetDisplayName gets a reference to the given string and assigns it to the DisplayName field.
+func (o *UserUpdate) SetDisplayName(v string) {
+	o.DisplayName = &v
+}
+
+// GetDrives returns the Drives field value if set, zero value otherwise.
+func (o *UserUpdate) GetDrives() []Drive {
+	if o == nil || IsNil(o.Drives) {
+		var ret []Drive
+		return ret
+	}
+	return o.Drives
+}
+
+// GetDrivesOk returns a tuple with the Drives field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetDrivesOk() ([]Drive, bool) {
+	if o == nil || IsNil(o.Drives) {
+		return nil, false
+	}
+	return o.Drives, true
+}
+
+// HasDrives returns a boolean if a field has been set.
+func (o *UserUpdate) HasDrives() bool {
+	if o != nil && !IsNil(o.Drives) {
+		return true
+	}
+
+	return false
+}
+
+// SetDrives gets a reference to the given []Drive and assigns it to the Drives field.
+func (o *UserUpdate) SetDrives(v []Drive) {
+	o.Drives = v
+}
+
+// GetDrive returns the Drive field value if set, zero value otherwise.
+func (o *UserUpdate) GetDrive() Drive {
+	if o == nil || IsNil(o.Drive) {
+		var ret Drive
+		return ret
+	}
+	return *o.Drive
+}
+
+// GetDriveOk returns a tuple with the Drive field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetDriveOk() (*Drive, bool) {
+	if o == nil || IsNil(o.Drive) {
+		return nil, false
+	}
+	return o.Drive, true
+}
+
+// HasDrive returns a boolean if a field has been set.
+func (o *UserUpdate) HasDrive() bool {
+	if o != nil && !IsNil(o.Drive) {
+		return true
+	}
+
+	return false
+}
+
+// SetDrive gets a reference to the given Drive and assigns it to the Drive field.
+func (o *UserUpdate) SetDrive(v Drive) {
+	o.Drive = &v
+}
+
+// GetIdentities returns the Identities field value if set, zero value otherwise.
+func (o *UserUpdate) GetIdentities() []ObjectIdentity {
+	if o == nil || IsNil(o.Identities) {
+		var ret []ObjectIdentity
+		return ret
+	}
+	return o.Identities
+}
+
+// GetIdentitiesOk returns a tuple with the Identities field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetIdentitiesOk() ([]ObjectIdentity, bool) {
+	if o == nil || IsNil(o.Identities) {
+		return nil, false
+	}
+	return o.Identities, true
+}
+
+// HasIdentities returns a boolean if a field has been set.
+func (o *UserUpdate) HasIdentities() bool {
+	if o != nil && !IsNil(o.Identities) {
+		return true
+	}
+
+	return false
+}
+
+// SetIdentities gets a reference to the given []ObjectIdentity and assigns it to the Identities field.
+func (o *UserUpdate) SetIdentities(v []ObjectIdentity) {
+	o.Identities = v
+}
+
+// GetMail returns the Mail field value if set, zero value otherwise.
+func (o *UserUpdate) GetMail() string {
+	if o == nil || IsNil(o.Mail) {
+		var ret string
+		return ret
+	}
+	return *o.Mail
+}
+
+// GetMailOk returns a tuple with the Mail field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetMailOk() (*string, bool) {
+	if o == nil || IsNil(o.Mail) {
+		return nil, false
+	}
+	return o.Mail, true
+}
+
+// HasMail returns a boolean if a field has been set.
+func (o *UserUpdate) HasMail() bool {
+	if o != nil && !IsNil(o.Mail) {
+		return true
+	}
+
+	return false
+}
+
+// SetMail gets a reference to the given string and assigns it to the Mail field.
+func (o *UserUpdate) SetMail(v string) {
+	o.Mail = &v
+}
+
+// GetMemberOf returns the MemberOf field value if set, zero value otherwise.
+func (o *UserUpdate) GetMemberOf() []Group {
+	if o == nil || IsNil(o.MemberOf) {
+		var ret []Group
+		return ret
+	}
+	return o.MemberOf
+}
+
+// GetMemberOfOk returns a tuple with the MemberOf field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetMemberOfOk() ([]Group, bool) {
+	if o == nil || IsNil(o.MemberOf) {
+		return nil, false
+	}
+	return o.MemberOf, true
+}
+
+// HasMemberOf returns a boolean if a field has been set.
+func (o *UserUpdate) HasMemberOf() bool {
+	if o != nil && !IsNil(o.MemberOf) {
+		return true
+	}
+
+	return false
+}
+
+// SetMemberOf gets a reference to the given []Group and assigns it to the MemberOf field.
+func (o *UserUpdate) SetMemberOf(v []Group) {
+	o.MemberOf = v
+}
+
+// GetOnPremisesSamAccountName returns the OnPremisesSamAccountName field value if set, zero value otherwise.
+func (o *UserUpdate) GetOnPremisesSamAccountName() string {
+	if o == nil || IsNil(o.OnPremisesSamAccountName) {
+		var ret string
+		return ret
+	}
+	return *o.OnPremisesSamAccountName
+}
+
+// GetOnPremisesSamAccountNameOk returns a tuple with the OnPremisesSamAccountName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetOnPremisesSamAccountNameOk() (*string, bool) {
+	if o == nil || IsNil(o.OnPremisesSamAccountName) {
+		return nil, false
+	}
+	return o.OnPremisesSamAccountName, true
+}
+
+// HasOnPremisesSamAccountName returns a boolean if a field has been set.
+func (o *UserUpdate) HasOnPremisesSamAccountName() bool {
+	if o != nil && !IsNil(o.OnPremisesSamAccountName) {
+		return true
+	}
+
+	return false
+}
+
+// SetOnPremisesSamAccountName gets a reference to the given string and assigns it to the OnPremisesSamAccountName field.
+func (o *UserUpdate) SetOnPremisesSamAccountName(v string) {
+	o.OnPremisesSamAccountName = &v
+}
+
+// GetPasswordProfile returns the PasswordProfile field value if set, zero value otherwise.
+func (o *UserUpdate) GetPasswordProfile() PasswordProfile {
+	if o == nil || IsNil(o.PasswordProfile) {
+		var ret PasswordProfile
+		return ret
+	}
+	return *o.PasswordProfile
+}
+
+// GetPasswordProfileOk returns a tuple with the PasswordProfile field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetPasswordProfileOk() (*PasswordProfile, bool) {
+	if o == nil || IsNil(o.PasswordProfile) {
+		return nil, false
+	}
+	return o.PasswordProfile, true
+}
+
+// HasPasswordProfile returns a boolean if a field has been set.
+func (o *UserUpdate) HasPasswordProfile() bool {
+	if o != nil && !IsNil(o.PasswordProfile) {
+		return true
+	}
+
+	return false
+}
+
+// SetPasswordProfile gets a reference to the given PasswordProfile and assigns it to the PasswordProfile field.
+func (o *UserUpdate) SetPasswordProfile(v PasswordProfile) {
+	o.PasswordProfile = &v
+}
+
+// GetSurname returns the Surname field value if set, zero value otherwise.
+func (o *UserUpdate) GetSurname() string {
+	if o == nil || IsNil(o.Surname) {
+		var ret string
+		return ret
+	}
+	return *o.Surname
+}
+
+// GetSurnameOk returns a tuple with the Surname field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetSurnameOk() (*string, bool) {
+	if o == nil || IsNil(o.Surname) {
+		return nil, false
+	}
+	return o.Surname, true
+}
+
+// HasSurname returns a boolean if a field has been set.
+func (o *UserUpdate) HasSurname() bool {
+	if o != nil && !IsNil(o.Surname) {
+		return true
+	}
+
+	return false
+}
+
+// SetSurname gets a reference to the given string and assigns it to the Surname field.
+func (o *UserUpdate) SetSurname(v string) {
+	o.Surname = &v
+}
+
+// GetGivenName returns the GivenName field value if set, zero value otherwise.
+func (o *UserUpdate) GetGivenName() string {
+	if o == nil || IsNil(o.GivenName) {
+		var ret string
+		return ret
+	}
+	return *o.GivenName
+}
+
+// GetGivenNameOk returns a tuple with the GivenName field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetGivenNameOk() (*string, bool) {
+	if o == nil || IsNil(o.GivenName) {
+		return nil, false
+	}
+	return o.GivenName, true
+}
+
+// HasGivenName returns a boolean if a field has been set.
+func (o *UserUpdate) HasGivenName() bool {
+	if o != nil && !IsNil(o.GivenName) {
+		return true
+	}
+
+	return false
+}
+
+// SetGivenName gets a reference to the given string and assigns it to the GivenName field.
+func (o *UserUpdate) SetGivenName(v string) {
+	o.GivenName = &v
+}
+
+// GetUserType returns the UserType field value if set, zero value otherwise.
+func (o *UserUpdate) GetUserType() string {
+	if o == nil || IsNil(o.UserType) {
+		var ret string
+		return ret
+	}
+	return *o.UserType
+}
+
+// GetUserTypeOk returns a tuple with the UserType field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetUserTypeOk() (*string, bool) {
+	if o == nil || IsNil(o.UserType) {
+		return nil, false
+	}
+	return o.UserType, true
+}
+
+// HasUserType returns a boolean if a field has been set.
+func (o *UserUpdate) HasUserType() bool {
+	if o != nil && !IsNil(o.UserType) {
+		return true
+	}
+
+	return false
+}
+
+// SetUserType gets a reference to the given string and assigns it to the UserType field.
+func (o *UserUpdate) SetUserType(v string) {
+	o.UserType = &v
+}
+
+// GetPreferredLanguage returns the PreferredLanguage field value if set, zero value otherwise.
+func (o *UserUpdate) GetPreferredLanguage() string {
+	if o == nil || IsNil(o.PreferredLanguage) {
+		var ret string
+		return ret
+	}
+	return *o.PreferredLanguage
+}
+
+// GetPreferredLanguageOk returns a tuple with the PreferredLanguage field value if set, nil otherwise
+// and a boolean to check if the value has been set.
+func (o *UserUpdate) GetPreferredLanguageOk() (*string, bool) {
+	if o == nil || IsNil(o.PreferredLanguage) {
+		return nil, false
+	}
+	return o.PreferredLanguage, true
+}
+
+// HasPreferredLanguage returns a boolean if a field has been set.
+func (o *UserUpdate) HasPreferredLanguage() bool {
+	if o != nil && !IsNil(o.PreferredLanguage) {
+		return true
+	}
+
+	return false
+}
+
+// SetPreferredLanguage gets a reference to the given string and assigns it to the PreferredLanguage field.
+func (o *UserUpdate) SetPreferredLanguage(v string) {
+	o.PreferredLanguage = &v
+}
+
+func (o UserUpdate) MarshalJSON() ([]byte, error) {
+	toSerialize, err := o.ToMap()
+	if err != nil {
+		return []byte{}, err
+	}
+	return json.Marshal(toSerialize)
+}
+
+func (o UserUpdate) ToMap() (map[string]interface{}, error) {
+	toSerialize := map[string]interface{}{}
+	if !IsNil(o.Id) {
+		toSerialize["id"] = o.Id
+	}
+	if !IsNil(o.AccountEnabled) {
+		toSerialize["accountEnabled"] = o.AccountEnabled
+	}
+	if !IsNil(o.AppRoleAssignments) {
+		toSerialize["appRoleAssignments"] = o.AppRoleAssignments
+	}
+	if !IsNil(o.DisplayName) {
+		toSerialize["displayName"] = o.DisplayName
+	}
+	if !IsNil(o.Drives) {
+		toSerialize["drives"] = o.Drives
+	}
+	if !IsNil(o.Drive) {
+		toSerialize["drive"] = o.Drive
+	}
+	if !IsNil(o.Identities) {
+		toSerialize["identities"] = o.Identities
+	}
+	if !IsNil(o.Mail) {
+		toSerialize["mail"] = o.Mail
+	}
+	if !IsNil(o.MemberOf) {
+		toSerialize["memberOf"] = o.MemberOf
+	}
+	if !IsNil(o.OnPremisesSamAccountName) {
+		toSerialize["onPremisesSamAccountName"] = o.OnPremisesSamAccountName
+	}
+	if !IsNil(o.PasswordProfile) {
+		toSerialize["passwordProfile"] = o.PasswordProfile
+	}
+	if !IsNil(o.Surname) {
+		toSerialize["surname"] = o.Surname
+	}
+	if !IsNil(o.GivenName) {
+		toSerialize["givenName"] = o.GivenName
+	}
+	if !IsNil(o.UserType) {
+		toSerialize["userType"] = o.UserType
+	}
+	if !IsNil(o.PreferredLanguage) {
+		toSerialize["preferredLanguage"] = o.PreferredLanguage
+	}
+	return toSerialize, nil
+}
+
+type NullableUserUpdate struct {
+	value *UserUpdate
+	isSet bool
+}
+
+func (v NullableUserUpdate) Get() *UserUpdate {
+	return v.value
+}
+
+func (v *NullableUserUpdate) Set(val *UserUpdate) {
+	v.value = val
+	v.isSet = true
+}
+
+func (v NullableUserUpdate) IsSet() bool {
+	return v.isSet
+}
+
+func (v *NullableUserUpdate) Unset() {
+	v.value = nil
+	v.isSet = false
+}
+
+func NewNullableUserUpdate(val *UserUpdate) *NullableUserUpdate {
+	return &NullableUserUpdate{value: val, isSet: true}
+}
+
+func (v NullableUserUpdate) MarshalJSON() ([]byte, error) {
+	return json.Marshal(v.value)
+}
+
+func (v *NullableUserUpdate) UnmarshalJSON(src []byte) error {
+	v.isSet = true
+	return json.Unmarshal(src, &v.value)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index bab8b11685a..e00865de0ef 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1625,7 +1625,7 @@ github.com/opentracing/opentracing-go/log
 # github.com/orcaman/concurrent-map v1.0.0
 ## explicit
 github.com/orcaman/concurrent-map
-# github.com/owncloud/libre-graph-api-go v1.0.5-0.20240618162722-2298241331d1
+# github.com/owncloud/libre-graph-api-go v1.0.5-0.20240816082515-afcf4367b966
 ## explicit; go 1.18
 github.com/owncloud/libre-graph-api-go
 # github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c


Test patch:
diff --git a/services/graph/pkg/identity/ldap_education_class_test.go b/services/graph/pkg/identity/ldap_education_class_test.go
index 1d5bf77e9f2..b57dccc7395 100644
--- a/services/graph/pkg/identity/ldap_education_class_test.go
+++ b/services/graph/pkg/identity/ldap_education_class_test.go
@@ -511,7 +511,7 @@ func TestLDAP_UpdateEducationClass(t *testing.T) {
 			args: args{
 				id: "abcd-defg",
 				class: libregraph.EducationClass{
-					Members: []libregraph.User{*libregraph.NewUser()},
+					Members: []libregraph.User{*libregraph.NewUser("display name", "username")},
 				},
 			},
 			assertion: func(tt assert.TestingT, err error, i ...interface{}) bool { return assert.Error(tt, err) },
diff --git a/services/graph/pkg/identity/ldap_test.go b/services/graph/pkg/identity/ldap_test.go
index 8d38ec83f98..8db390b55d0 100644
--- a/services/graph/pkg/identity/ldap_test.go
+++ b/services/graph/pkg/identity/ldap_test.go
@@ -135,10 +135,8 @@ func TestCreateUser(t *testing.T) {
 	l.On("Add", ar).Return(nil)
 	logger := log.NewLogger(log.Level("debug"))
 
-	user := libregraph.NewUser()
-	user.SetDisplayName(displayName)
+	user := libregraph.NewUser(displayName, userName)
 	user.SetMail(mail)
-	user.SetOnPremisesSamAccountName(userName)
 	user.SetSurname(surname)
 	user.SetGivenName(givenName)
 	user.SetAccountEnabled(true)
@@ -171,14 +169,14 @@ func TestCreateUserModelFromLDAP(t *testing.T) {
 	if user == nil {
 		t.Error("Converting a valid LDAP Entry should succeed")
 	} else {
-		if *user.OnPremisesSamAccountName != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.userName) {
+		if user.OnPremisesSamAccountName != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.userName) {
 			t.Errorf("Error creating msGraph User from LDAP Entry: %v != %v", user.OnPremisesSamAccountName, pointerOrNil(userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.userName)))
 		}
 		if *user.Mail != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.mail) {
 			t.Errorf("Error creating msGraph User from LDAP Entry: %s != %s", *user.Mail, userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.mail))
 		}
-		if *user.DisplayName != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.displayName) {
-			t.Errorf("Error creating msGraph User from LDAP Entry: %s != %s", *user.DisplayName, userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.displayName))
+		if user.DisplayName != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.displayName) {
+			t.Errorf("Error creating msGraph User from LDAP Entry: %s != %s", user.DisplayName, userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.displayName))
 		}
 		if *user.Id != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.id) {
 			t.Errorf("Error creating msGraph User from LDAP Entry: %s != %s", *user.Id, userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.id))
@@ -1447,7 +1445,7 @@ func TestUpdateUser(t *testing.T) {
 			ldapConfig.DisableUserMechanism = tt.args.disableUserMechanism
 			i, _ := getMockedBackend(lm, ldapConfig, &logger)
 
-			user := libregraph.User{
+			user := libregraph.UserUpdate{
 				Id:                       &tt.args.userProps.id,
 				Mail:                     &tt.args.userProps.mail,
 				DisplayName:              &tt.args.userProps.displayName,
@@ -1462,15 +1460,15 @@ func TestUpdateUser(t *testing.T) {
 				want = &libregraph.User{
 					Id:                       &tt.want.id,
 					Mail:                     &tt.want.mail,
-					DisplayName:              &tt.want.displayName,
-					OnPremisesSamAccountName: &tt.want.onPremisesSamAccountName,
+					DisplayName:              tt.want.displayName,
+					OnPremisesSamAccountName: tt.want.onPremisesSamAccountName,
 					Surname:                  &emptyString,
 					GivenName:                tt.want.givenName,
 					UserType:                 tt.want.userType,
 				}
 
 				if tt.want.accountEnabled != nil {
-					want.AccountEnabled = *&tt.want.accountEnabled
+					want.AccountEnabled = tt.want.accountEnabled
 				}
 			}
 
diff --git a/services/graph/pkg/service/v0/educationschools_test.go b/services/graph/pkg/service/v0/educationschools_test.go
index d8885462c5b..3b291bcb708 100644
--- a/services/graph/pkg/service/v0/educationschools_test.go
+++ b/services/graph/pkg/service/v0/educationschools_test.go
@@ -394,6 +394,9 @@ var _ = Describe("Schools", func() {
 	Describe("GetEducationSchoolUsers", func() {
 		It("gets the list of members", func() {
 			user := libregraph.NewEducationUser()
+			user.SetOnPremisesSamAccountName("user")
+			user.SetDisplayName("display name")
+			user.SetMail("mail")
 			user.SetId("user")
 			identityEducationBackend.On("GetEducationSchoolUsers", mock.Anything, mock.Anything, mock.Anything).Return([]*libregraph.EducationUser{user}, nil)
 
@@ -520,12 +523,12 @@ var _ = Describe("Schools", func() {
 			data, err := io.ReadAll(rr.Body)
 			Expect(err).ToNot(HaveOccurred())
 
-			var members []*libregraph.User
-			err = json.Unmarshal(data, &members)
+			var classes []*libregraph.EducationClass
+			err = json.Unmarshal(data, &classes)
 			Expect(err).ToNot(HaveOccurred())
 
-			Expect(len(members)).To(Equal(1))
-			Expect(members[0].GetId()).To(Equal("class"))
+			Expect(len(classes)).To(Equal(1))
+			Expect(classes[0].GetId()).To(Equal("class"))
 		})
 	})
 
diff --git a/services/graph/pkg/service/v0/groups_test.go b/services/graph/pkg/service/v0/groups_test.go
index 41ebcdf7a21..b83f3b1626f 100644
--- a/services/graph/pkg/service/v0/groups_test.go
+++ b/services/graph/pkg/service/v0/groups_test.go
@@ -239,7 +239,7 @@ var _ = Describe("Groups", func() {
 			group.SetDisplayName("Group Name")
 			group.SetMembers(
 				[]libregraph.User{
-					libregraph.User{
+					{
 						Id: libregraph.PtrString("userid"),
 					},
 				},
@@ -259,7 +259,7 @@ var _ = Describe("Groups", func() {
 			Expect(err).ToNot(HaveOccurred())
 			groupMap, err := res.Value[0].ToMap()
 			Expect(err).ToNot(HaveOccurred())
-			for k, _ := range groupMap {
+			for k := range groupMap {
 				Expect(k).Should(BeElementOf([]string{"displayName", "id", "groupTypes"}))
 			}
 		})
@@ -523,8 +523,8 @@ var _ = Describe("Groups", func() {
 
 	Describe("GetGroupMembers", func() {
 		It("gets the list of members", func() {
-			user := libregraph.NewUser()
-			user.SetId("user")
+			user := libregraph.NewUser("display name", "username")
+			user.SetId("userid")
 			identityBackend.On("GetGroupMembers", mock.Anything, mock.Anything, mock.Anything).Return([]*libregraph.User{user}, nil)
 
 			r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/groups/{groupID}/members", nil)
@@ -542,7 +542,7 @@ var _ = Describe("Groups", func() {
 			Expect(err).ToNot(HaveOccurred())
 
 			Expect(len(members)).To(Equal(1))
-			Expect(members[0].GetId()).To(Equal("user"))
+			Expect(members[0].GetId()).To(Equal("userid"))
 		})
 	})
 
diff --git a/services/graph/pkg/service/v0/users_test.go b/services/graph/pkg/service/v0/users_test.go
index f18d101d3d1..ac7ae6958b9 100644
--- a/services/graph/pkg/service/v0/users_test.go
+++ b/services/graph/pkg/service/v0/users_test.go
@@ -301,7 +301,7 @@ var _ = Describe("Users", func() {
 
 			It("only returns a restricted set of attributes for unprivileged users", func() {
 				permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
-				user := &libregraph.User{}
+				user := libregraph.NewUser("abc user", "username")
 				user.SetId("user1")
 				user.SetPasswordProfile(
 					libregraph.PasswordProfile{
@@ -309,7 +309,6 @@ var _ = Describe("Users", func() {
 					},
 				)
 				user.SetUserType("usertype")
-				user.SetDisplayName("abc user")
 				users := []*libregraph.User{user}
 
 				identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
@@ -325,8 +324,8 @@ var _ = Describe("Users", func() {
 				Expect(err).ToNot(HaveOccurred())
 				userMap, err := res.Value[0].ToMap()
 				Expect(err).ToNot(HaveOccurred())
-				for k, _ := range userMap {
-					Expect(k).Should(BeElementOf([]string{"mail", "displayName", "id", "userType"}))
+				for k := range userMap {
+					Expect(k).Should(BeElementOf([]string{"displayName", "onPremisesSamAccountName", "mail", "id", "userType"}))
 				}
 			})
 
@@ -800,10 +799,7 @@ var _ = Describe("Users", func() {
 			)
 
 			BeforeEach(func() {
-				user = &libregraph.User{}
-				user.SetDisplayName("Display Name")
-				user.SetOnPremisesSamAccountName("user")
-				user.SetMail("user@example.com")
+				user = libregraph.NewUser("Display Name", "user")
 			})
 
 			It("handles invalid bodies", func() {
@@ -814,15 +810,11 @@ var _ = Describe("Users", func() {
 			})
 
 			It("handles missing display names", func() {
-				user.DisplayName = nil
+				user.SetDisplayName("")
 				assertHandleBadAttributes(user)
-
 			})
 
 			It("handles missing OnPremisesSamAccountName", func() {
-				user.OnPremisesSamAccountName = nil
-				assertHandleBadAttributes(user)
-
 				user.SetOnPremisesSamAccountName("")
 				assertHandleBadAttributes(user)
 			})
@@ -1034,16 +1026,23 @@ var _ = Describe("Users", func() {
 
 		Describe("PatchUser", func() {
 			var (
-				user *libregraph.User
+				user         *libregraph.User
+				userUpdate   *libregraph.UserUpdate
+				expectedUser *libregraph.User
 			)
 
 			BeforeEach(func() {
-				user = &libregraph.User{}
-				user.SetDisplayName("Display Name")
-				user.SetOnPremisesSamAccountName("user")
+				user = libregraph.NewUser("Display Name", "user")
 				user.SetMail("user@example.com")
 				user.SetId("/users/user")
 
+				userUpdate = libregraph.NewUserUpdate()
+				userUpdate.SetId(user.GetId())
+
+				expectedUser = libregraph.NewUser("Display Name", "user")
+				expectedUser.SetMail(user.GetMail())
+				expectedUser.SetId(user.GetId())
+
 				identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
 			})
 
@@ -1065,13 +1064,13 @@ var _ = Describe("Users", func() {
 			})
 
 			It("handles invalid email", func() {
-				user.SetMail("invalid")
-				data, err := json.Marshal(user)
+				userUpdate.SetMail("invalid")
+				data, err := json.Marshal(userUpdate)
 				Expect(err).ToNot(HaveOccurred())
 
 				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
 				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
+				rctx.URLParams.Add("userID", userUpdate.GetId())
 				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
 				svc.PatchUser(rr, r)
 
@@ -1079,13 +1078,13 @@ var _ = Describe("Users", func() {
 			})
 
 			It("handles invalid userType", func() {
-				user.SetUserType("Clown")
-				data, err := json.Marshal(user)
+				userUpdate.SetUserType("Clown")
+				data, err := json.Marshal(userUpdate)
 				Expect(err).ToNot(HaveOccurred())
 
 				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users?$invalid=true", bytes.NewBuffer(data))
 				rctx := chi.NewRouteContext()
-				rctx.URLParams.Add("userID", user.GetId())
+				rctx.URLParams.Add("userID", userUpdate.GetId())
 				r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
 				svc.PatchUser(rr, r)
 
@@ -1093,12 +1092,14 @@ var _ = Describe("Users", func() {
 			})
 
 			It("updates attributes", func() {
-				user.SetUserType("Member")
-				identityBackend.On("UpdateUser", mock.Anything, user.GetId(), mock.Anything).Return(user, nil)
+				userUpdate.SetUserType("Member")
+				userUpdate.SetDisplayName("New Display Name")
+
+				expectedUser.SetUserType("Member")
+				expectedUser.SetDisplayName("New Display Name")
+				identityBackend.On("UpdateUser", mock.Anything, userUpdate.GetId(), mock.Anything).Return(expectedUser, nil)
 
-				user.SetUserType(("Member"))
-				user.SetDisplayName("New Display Name")
-				data, err := json.Marshal(user)
+				data, err := json.Marshal(userUpdate)
 				Expect(err).ToNot(HaveOccurred())
 
 				r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users", bytes.NewBuffer(data))
@@ -1111,11 +1112,11 @@ var _ = Describe("Users", func() {
 				data, err = io.ReadAll(rr.Body)
 				Expect(err).ToNot(HaveOccurred())
 
-				updatedUser := libregraph.User{}
-				err = json.Unmarshal(data, &updatedUser)
+				unmarshaledUser := libregraph.User{}
+				err = json.Unmarshal(data, &unmarshaledUser)
 				Expect(err).ToNot(HaveOccurred())
-				Expect(updatedUser.GetUserType()).To(Equal("Member"))
-				Expect(updatedUser.GetDisplayName()).To(Equal("New Display Name"))
+				Expect(unmarshaledUser.GetUserType()).To(Equal("Member"))
+				Expect(unmarshaledUser.GetDisplayName()).To(Equal("New Display Name"))
 			})
 		})
 	})