From 336f128e715a314c19f692de5a450d38ab94a964 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 15 Dec 2022 19:36:19 +0100 Subject: [PATCH] Add support for assigning education users to schools --- services/graph/pkg/identity/ldap_school.go | 66 ++++++++++++++++--- .../graph/pkg/identity/ldap_school_test.go | 43 ++++++++++++ 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/services/graph/pkg/identity/ldap_school.go b/services/graph/pkg/identity/ldap_school.go index d3413cc45b..8a1ed6cea3 100644 --- a/services/graph/pkg/identity/ldap_school.go +++ b/services/graph/pkg/identity/ldap_school.go @@ -15,11 +15,14 @@ import ( ) type educationConfig struct { - schoolBaseDN string - schoolFilter string - schoolObjectClass string - schoolScope int - schoolAttributeMap schoolAttributeMap + schoolBaseDN string + schoolFilter string + schoolObjectClass string + schoolScope int + // memberOfSchoolAttribute defines the AttributeType on the user/group objects + // which contains the school Id to which the user/group is assigned + memberOfSchoolAttribute string + schoolAttributeMap schoolAttributeMap userObjectClass string userAttributeMap educationUserAttributeMap @@ -33,9 +36,10 @@ type schoolAttributeMap struct { func defaultEducationConfig() educationConfig { return educationConfig{ - schoolObjectClass: "ocEducationSchool", - schoolScope: ldap.ScopeWholeSubtree, - schoolAttributeMap: newSchoolAttributeMap(), + schoolObjectClass: "ocEducationSchool", + schoolScope: ldap.ScopeWholeSubtree, + memberOfSchoolAttribute: "ocMemberOfSchool", + schoolAttributeMap: newSchoolAttributeMap(), userObjectClass: "ocEducationUser", userAttributeMap: newEducationUserAttributeMap(), @@ -140,6 +144,8 @@ func (i *LDAP) DeleteEducationSchool(ctx context.Context, id string) error { if err = i.conn.Del(&dr); err != nil { return err } + + // TODO update any users that are member of this school return nil } @@ -206,8 +212,48 @@ func (i *LDAP) GetEducationSchoolMembers(ctx context.Context, id string) ([]*lib } // AddMembersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend. -func (i *LDAP) AddMembersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error { - return errNotImplemented +func (i *LDAP) AddMembersToEducationSchool(ctx context.Context, schoolID string, memberIDs []string) error { + logger := i.logger.SubloggerWithRequestID(ctx) + logger.Debug().Str("backend", "ldap").Msg("AddMembersToEducationSchool") + + schoolEntry, err := i.getSchoolByID(schoolID) + if err != nil { + return err + } + + if schoolEntry == nil { + return errNotFound + } + + userEntries := make([]*ldap.Entry, 0, len(memberIDs)) + for _, memberID := range memberIDs { + user, err := i.getEducationUserByNameOrID(memberID) + if err != nil { + i.logger.Warn().Str("userid", memberID).Msg("User does not exist") + return err + } + userEntries = append(userEntries, user) + } + + for _, userEntry := range userEntries { + currentSchools := userEntry.GetEqualFoldAttributeValues(i.educationConfig.memberOfSchoolAttribute) + found := false + for _, currentSchool := range currentSchools { + if currentSchool == schoolID { + found = true + break + } + } + if !found { + mr := ldap.ModifyRequest{DN: userEntry.DN} + mr.Add(i.educationConfig.memberOfSchoolAttribute, []string{schoolID}) + if err := i.conn.Modify(&mr); err != nil { + return err + } + } + } + + return nil } // RemoveMemberFromEducationSchool removes a single member (by ID) from a school diff --git a/services/graph/pkg/identity/ldap_school_test.go b/services/graph/pkg/identity/ldap_school_test.go index c6a4e65171..3d4d5f8f27 100644 --- a/services/graph/pkg/identity/ldap_school_test.go +++ b/services/graph/pkg/identity/ldap_school_test.go @@ -167,3 +167,46 @@ func TestGetEducationSchools(t *testing.T) { lm.AssertNumberOfCalls(t, "Search", 1) assert.Nil(t, err) } + +func TestAddMembersToEducationSchool(t *testing.T) { + lm := &mocks.Client{} + sr1 := &ldap.SearchRequest{ + BaseDN: "", + Scope: 2, + SizeLimit: 1, + Filter: "(&(objectClass=ocEducationSchool)(owncloudUUID=abcd-defg))", + Attributes: []string{"ou", "owncloudUUID", "ocEducationSchoolNumber"}, + Controls: []ldap.Control(nil), + } + sr2 := &ldap.SearchRequest{ + BaseDN: "ou=people,dc=test", + Scope: 2, + SizeLimit: 1, + Filter: "(&(objectClass=ocEducationUser)(|(uid=abcd-defg)(entryUUID=abcd-defg)))", + Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass"}, + Controls: []ldap.Control(nil), + } + sr3 := &ldap.SearchRequest{ + BaseDN: "ou=people,dc=test", + Scope: 2, + SizeLimit: 1, + Filter: "(&(objectClass=ocEducationUser)(|(uid=does-not-exist)(entryUUID=does-not-exist)))", + Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass"}, + Controls: []ldap.Control(nil), + } + lm.On("Search", sr1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{schoolEntry, schoolEntry1}}, nil) + lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil) + lm.On("Search", sr3).Return(&ldap.SearchResult{Entries: []*ldap.Entry{}}, nil) + lm.On("Modify", mock.Anything).Return(nil) + b, err := getMockedBackend(lm, eduConfig, &logger) + assert.Nil(t, err) + err = b.AddMembersToEducationSchool(context.Background(), "abcd-defg", []string{"does-not-exist"}) + lm.AssertNumberOfCalls(t, "Search", 2) + assert.NotNil(t, err) + err = b.AddMembersToEducationSchool(context.Background(), "abcd-defg", []string{"abcd-defg", "does-not-exist"}) + lm.AssertNumberOfCalls(t, "Search", 5) + assert.NotNil(t, err) + err = b.AddMembersToEducationSchool(context.Background(), "abcd-defg", []string{"abcd-defg"}) + lm.AssertNumberOfCalls(t, "Search", 7) + assert.Nil(t, err) +}