Add LDAP entries for adding classes to schools.

This commit is contained in:
Daniël Franke
2023-01-31 12:52:49 +01:00
committed by Ralf Haferkamp
parent e9bc2eeb2b
commit 9b8adb65ed
3 changed files with 182 additions and 49 deletions
@@ -341,16 +341,14 @@ func (i *LDAP) getEducationClassLDAPDN(class libregraph.EducationClass) string {
return fmt.Sprintf("ocEducationExternalId=%s,%s", oldap.EscapeDNAttributeValue(class.GetExternalId()), i.groupBaseDN)
}
// getEducationClassByID looks up a class by id (and be ID or externalID)
func (i *LDAP) getEducationClassByID(id string, requestMembers bool) (*ldap.Entry, error) {
id = ldap.EscapeFilter(id)
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))",
i.groupAttributeMap.id, id,
i.educationConfig.classAttributeMap.externalID, id)
return i.getEducationClassByFilter(filter, requestMembers)
}
func (i *LDAP) getEducationClassByFilter(filter string, requestMembers bool) (*ldap.Entry, error) {
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.groupFilter, i.educationConfig.classObjectClass, filter)
return i.searchLDAPEntryByFilter(i.groupBaseDN, i.getEducationClassAttrTypes(requestMembers), filter)
func (i *LDAP) getEducationClassByID(nameOrID string, requestMembers bool) (*ldap.Entry, error) {
return i.getEducationObjectByNameOrID(
nameOrID,
i.groupAttributeMap.name,
i.groupAttributeMap.id,
i.groupFilter,
i.educationConfig.classObjectClass,
i.groupBaseDN,
i.getEducationClassAttrTypes(requestMembers),
)
}
@@ -10,6 +10,7 @@ import (
"github.com/gofrs/uuid"
libregraph "github.com/owncloud/libre-graph-api-go"
oldap "github.com/owncloud/ocis/v2/ocis-pkg/ldap"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
)
@@ -352,42 +353,16 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, schoolNumberOrID str
logger := i.logger.SubloggerWithRequestID(ctx)
logger.Debug().Str("backend", "ldap").Msg("GetEducationSchoolUsers")
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
entries, err := i.getEducationSchoolEntries(
schoolNumberOrID, i.userFilter, i.educationConfig.userObjectClass, i.userBaseDN, i.userScope, i.getUserAttrTypes(), logger,
)
if err != nil {
return nil, err
}
if schoolEntry == nil {
return nil, ErrNotFound
}
schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
schoolID = ldap.EscapeFilter(schoolID)
idFilter := fmt.Sprintf("(%s=%s)", i.educationConfig.memberOfSchoolAttribute, schoolID)
userFilter := fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.educationConfig.userObjectClass, idFilter)
users := make([]*libregraph.EducationUser, 0, len(entries))
searchRequest := ldap.NewSearchRequest(
i.userBaseDN,
i.userScope,
ldap.NeverDerefAliases, 0, 0, false,
userFilter,
i.getEducationUserAttrTypes(),
nil,
)
logger.Debug().Str("backend", "ldap").
Str("base", searchRequest.BaseDN).
Str("filter", searchRequest.Filter).
Int("scope", searchRequest.Scope).
Int("sizelimit", searchRequest.SizeLimit).
Interface("attributes", searchRequest.Attributes).
Msg("GetEducationUsers")
res, err := i.conn.Search(searchRequest)
if err != nil {
return nil, errorcode.New(errorcode.ItemNotFound, err.Error())
}
users := make([]*libregraph.EducationUser, 0, len(res.Entries))
for _, e := range res.Entries {
for _, e := range entries {
u := i.createEducationUserModelFromLDAP(e)
// Skip invalid LDAP users
if u == nil {
@@ -479,6 +454,154 @@ func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOr
return nil
}
// GetEducationSchoolClasses implements the EducationBackend interface for the LDAP backend.
func (i *LDAP) GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID string) ([]*libregraph.EducationClass, error) {
logger := i.logger.SubloggerWithRequestID(ctx)
logger.Debug().Str("backend", "ldap").Msg("GetEducationSchoolClasses")
entries, err := i.getEducationSchoolEntries(
schoolNumberOrID, i.groupFilter, i.educationConfig.classObjectClass, i.groupBaseDN, i.groupScope, i.getEducationClassAttrTypes(false), logger, // TODO: Add attributes
)
if err != nil {
return nil, err
}
users := make([]*libregraph.EducationClass, 0, len(entries))
for _, e := range entries {
u := i.createEducationClassModelFromLDAP(e)
// Skip invalid LDAP classes
if u == nil {
continue
}
users = append(users, u)
}
return users, nil
}
func (i *LDAP) getEducationSchoolEntries(
schoolNumberOrID, filter, objectClass, baseDN string,
scope int,
attributes []string,
logger log.Logger,
) ([]*ldap.Entry, error) {
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
if err != nil {
return nil, err
}
if schoolEntry == nil {
return nil, ErrNotFound
}
schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
schoolID = ldap.EscapeFilter(schoolID)
idFilter := fmt.Sprintf("(%s=%s)", i.educationConfig.memberOfSchoolAttribute, schoolID)
searchFilter := fmt.Sprintf("(&%s(objectClass=%s)%s)", filter, objectClass, idFilter)
searchRequest := ldap.NewSearchRequest(
baseDN,
scope,
ldap.NeverDerefAliases, 0, 0, false,
searchFilter,
attributes,
nil,
)
logger.Debug().Str("backend", "ldap").
Str("base", searchRequest.BaseDN).
Str("filter", searchRequest.Filter).
Int("scope", searchRequest.Scope).
Int("sizelimit", searchRequest.SizeLimit).
Interface("attributes", searchRequest.Attributes).
Msg("GetEducationClasses")
res, err := i.conn.Search(searchRequest)
if err != nil {
return nil, errorcode.New(errorcode.ItemNotFound, err.Error())
}
return res.Entries, nil
}
// AddClassesToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
logger := i.logger.SubloggerWithRequestID(ctx)
logger.Debug().Str("backend", "ldap").Msg("AddClassesToEducationSchool")
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
if err != nil {
return err
}
if schoolEntry == nil {
return ErrNotFound
}
schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
classEntries := make([]*ldap.Entry, 0, len(memberIDs))
for _, memberID := range memberIDs {
class, err := i.getEducationClassByID(memberID, false)
if err != nil {
i.logger.Warn().Str("userid", memberID).Msg("Class does not exist")
return err
}
classEntries = append(classEntries, class)
}
for _, classEntry := range classEntries {
currentSchools := classEntry.GetEqualFoldAttributeValues(i.educationConfig.memberOfSchoolAttribute)
found := false
for _, currentSchool := range currentSchools {
if currentSchool == schoolID {
found = true
break
}
}
if !found {
mr := ldap.ModifyRequest{DN: classEntry.DN}
mr.Add(i.educationConfig.memberOfSchoolAttribute, []string{schoolID})
if err := i.conn.Modify(&mr); err != nil {
return err
}
}
}
return nil
}
// RemoveClassFromEducationSchool removes a single member (by ID) from a school
func (i *LDAP) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
logger := i.logger.SubloggerWithRequestID(ctx)
logger.Debug().Str("backend", "ldap").Msg("RemoveClassFromEducationSchool")
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
if err != nil {
return err
}
if schoolEntry == nil {
return ErrNotFound
}
schoolID := schoolEntry.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
user, err := i.getEducationClassByID(memberID, false)
if err != nil {
i.logger.Warn().Str("userid", memberID).Msg("Class does not exist")
return err
}
currentSchools := user.GetEqualFoldAttributeValues(i.educationConfig.memberOfSchoolAttribute)
for _, currentSchool := range currentSchools {
if currentSchool == schoolID {
mr := ldap.ModifyRequest{DN: user.DN}
mr.Delete(i.educationConfig.memberOfSchoolAttribute, []string{schoolID})
if err := i.conn.Modify(&mr); err != nil {
return err
}
break
}
}
return nil
}
func (i *LDAP) getSchoolByDN(dn string) (*ldap.Entry, error) {
attrs := []string{
i.educationConfig.schoolAttributeMap.displayName,
@@ -265,12 +265,24 @@ func (i *LDAP) getEducationUserByDN(dn string) (*ldap.Entry, error) {
}
func (i *LDAP) getEducationUserByNameOrID(nameOrID string) (*ldap.Entry, error) {
nameOrID = ldap.EscapeFilter(nameOrID)
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))", i.userAttributeMap.userName, nameOrID, i.userAttributeMap.id, nameOrID)
return i.getEducationUserByFilter(filter)
return i.getEducationObjectByNameOrID(
nameOrID,
i.userAttributeMap.userName,
i.userAttributeMap.id,
i.userFilter,
i.educationConfig.userObjectClass,
i.userBaseDN,
i.getEducationUserAttrTypes(),
)
}
func (i *LDAP) getEducationUserByFilter(filter string) (*ldap.Entry, error) {
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.educationConfig.userObjectClass, filter)
return i.searchLDAPEntryByFilter(i.userBaseDN, i.getEducationUserAttrTypes(), filter)
func (i *LDAP) getEducationObjectByNameOrID(nameOrID, nameAttribute, idAttribute, objectFilter, objectClass, baseDN string, attributes []string) (*ldap.Entry, error) {
nameOrID = ldap.EscapeFilter(nameOrID)
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))", nameAttribute, nameOrID, idAttribute, nameOrID)
return i.getEducationObjectByFilter(filter, baseDN, objectFilter, objectClass, attributes)
}
func (i *LDAP) getEducationObjectByFilter(filter, baseDN, objectFilter, objectClass string, attributes []string) (*ldap.Entry, error) {
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", objectFilter, objectClass, filter)
return i.searchLDAPEntryByFilter(baseDN, attributes, filter)
}