From 2ca35f0a2bc096cb9c2d4ae5948758656d9fea77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Franke?= Date: Tue, 17 Jan 2023 13:52:04 +0100 Subject: [PATCH] Allow patching of EducationClass properties. As described in #5410: add support for updating educationClass properties. This adds the `UpdateEducationClass` to the `EducationBackend` interface, and implements it on the `ErrEducationBackend` and `LDAP` backends. It also alters `PatchEducationClass` to call the `UpdateEducationClass` method. Closes #5410 --- services/graph/pkg/identity/backend.go | 2 + services/graph/pkg/identity/err_education.go | 4 ++ .../pkg/identity/ldap_education_class.go | 72 +++++++++++++++++++ .../graph/pkg/service/v0/educationclasses.go | 14 +++- 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/services/graph/pkg/identity/backend.go b/services/graph/pkg/identity/backend.go index 5b8d5f5f44..440909f3b7 100644 --- a/services/graph/pkg/identity/backend.go +++ b/services/graph/pkg/identity/backend.go @@ -70,6 +70,8 @@ type EducationBackend interface { DeleteEducationClass(ctx context.Context, nameOrID string) error // GetEducationClassMembers returns the EducationUser members for an EducationClass GetEducationClassMembers(ctx context.Context, nameOrID string) ([]*libregraph.EducationUser, error) + // UpdateEducationClass updates properties of the supplied class in the identity backend. + UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) // CreateEducationUser creates a given education user in the identity backend. CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) diff --git a/services/graph/pkg/identity/err_education.go b/services/graph/pkg/identity/err_education.go index 6e81f7c9c5..b628d0dcc8 100644 --- a/services/graph/pkg/identity/err_education.go +++ b/services/graph/pkg/identity/err_education.go @@ -75,6 +75,10 @@ func (i *ErrEducationBackend) GetEducationClassMembers(ctx context.Context, name return nil, errNotImplemented } +func (i *ErrEducationBackend) UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) { + return nil, errNotImplemented +} + // CreateEducationUser creates a given education user in the identity backend. func (i *ErrEducationBackend) CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) { return nil, errNotImplemented diff --git a/services/graph/pkg/identity/ldap_education_class.go b/services/graph/pkg/identity/ldap_education_class.go index 93ba9b1376..1ce414ab73 100644 --- a/services/graph/pkg/identity/ldap_education_class.go +++ b/services/graph/pkg/identity/ldap_education_class.go @@ -139,6 +139,78 @@ func (i *LDAP) DeleteEducationClass(ctx context.Context, id string) error { return nil } +// UpdateEducationClass implements the EducationBackend interface for the LDAP backend. +// Only the displayName and externalID are supported to change at this point. +func (i *LDAP) UpdateEducationClass(ctx context.Context, id string, class libregraph.EducationClass) (*libregraph.EducationClass, error) { + logger := i.logger.SubloggerWithRequestID(ctx) + logger.Debug().Str("backend", "ldap").Msg("UpdateEducationClass") + if !i.writeEnabled { + return nil, ErrReadOnly + } + + g, err := i.getLDAPGroupByID(id, false) + if err != nil { + return nil, err + } + + var updateNeeded bool + + if class.GetId() != "" { + if g.GetEqualFoldAttributeValue(i.groupAttributeMap.id) != class.GetId() { + return nil, errorcode.New(errorcode.NotAllowed, "changing the GroupID is not allowed") + } + } + + if class.GetOnPremisesSamAccountName() != "" { + return nil, errorcode.New(errorcode.NotSupported, "changing the SAM account name is currently not supported") + } + + if class.GetOnPremisesDomainName() != "" { + return nil, errorcode.New(errorcode.NotSupported, "changing the SAM account name is currently not supported") + } + + if class.GetDescription() != "" { + return nil, errorcode.New(errorcode.NotSupported, "changing the description is currently not supported") + } + + if len(class.GetMembers()) != 0 { + return nil, errorcode.New(errorcode.NotSupported, "changing the members is currently not supported") + } + + if class.GetClassification() != "" { + return nil, errorcode.New(errorcode.NotSupported, "changing the classification is currently not supported") + } + + mr := ldap.ModifyRequest{DN: g.DN} + + if eID := class.GetExternalId(); eID != "" { + if g.GetEqualFoldAttributeValue(i.educationConfig.classAttributeMap.externalID) != eID { + mr.Replace(i.educationConfig.classAttributeMap.externalID, []string{eID}) + updateNeeded = true + } + } + + if dName := class.GetDisplayName(); dName != "" { + if g.GetEqualFoldAttributeValue(i.groupAttributeMap.name) != dName { + mr.Replace(i.groupAttributeMap.name, []string{dName}) + updateNeeded = true + } + } + + if updateNeeded { + if err := i.conn.Modify(&mr); err != nil { + return nil, err + } + } + + g, err = i.getEducationClassByDN(g.DN) + if err != nil { + return nil, err + } + + return i.createEducationClassModelFromLDAP(g), nil +} + // GetEducationClassMembers implements the EducationBackend interface for the LDAP backend. func (i *LDAP) GetEducationClassMembers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) { logger := i.logger.SubloggerWithRequestID(ctx) diff --git a/services/graph/pkg/service/v0/educationclasses.go b/services/graph/pkg/service/v0/educationclasses.go index d50a1768ee..7571b91a0a 100644 --- a/services/graph/pkg/service/v0/educationclasses.go +++ b/services/graph/pkg/service/v0/educationclasses.go @@ -120,6 +120,16 @@ func (g Graph) PatchEducationClass(w http.ResponseWriter, r *http.Request) { return } + class, err := g.identityEducationBackend.UpdateEducationClass(r.Context(), classID, *changes) + if err != nil { + logger.Error(). + Err(err). + Str("classID", classID). + Msg("could not update class") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error()) + return + } + if memberRefs, ok := changes.GetMembersodataBindOk(); ok { // The spec defines a limit of 20 members maxium per Request if len(memberRefs) > g.config.API.GroupMembersPatchLimit { @@ -166,8 +176,8 @@ func (g Graph) PatchEducationClass(w http.ResponseWriter, r *http.Request) { } return } - render.Status(r, http.StatusNoContent) // TODO StatusNoContent when prefer=minimal is used, otherwise OK and the resource in the body - render.NoContent(w, r) + render.Status(r, http.StatusOK) // TODO StatusNoContent when prefer=minimal is used, otherwise OK and the resource in the body + render.JSON(w, r, class) } // GetEducationClass implements the Service interface.