diff --git a/devtools/deployments/multi-tenancy/initialize_users.go b/devtools/deployments/multi-tenancy/initialize_users.go index ebbbbb1073..9c64182b44 100644 --- a/devtools/deployments/multi-tenancy/initialize_users.go +++ b/devtools/deployments/multi-tenancy/initialize_users.go @@ -24,6 +24,7 @@ var demoTenants = []tenantWithUsers{ { tenant: libregraph.EducationSchool{ DisplayName: libregraph.PtrString("Famous Coders"), + ExternalId: libregraph.PtrString("famouscodersExternalID"), }, users: []libregraph.EducationUser{ { @@ -41,6 +42,7 @@ var demoTenants = []tenantWithUsers{ { tenant: libregraph.EducationSchool{ DisplayName: libregraph.PtrString("Scientists"), + ExternalId: libregraph.PtrString("scientistsExternalID"), }, users: []libregraph.EducationUser{ { diff --git a/devtools/deployments/multi-tenancy/traefik.yml b/devtools/deployments/multi-tenancy/traefik.yml index 528cc09b6a..c450f2902e 100644 --- a/devtools/deployments/multi-tenancy/traefik.yml +++ b/devtools/deployments/multi-tenancy/traefik.yml @@ -9,7 +9,7 @@ services: - "traefik.http.services.opencloud.loadbalancer.server.port=9200" - "traefik.http.routers.opencloud.${TRAEFIK_SERVICES_TLS_CONFIG}" traefik: - image: traefik:v3.3.1 + image: traefik:v3.6.7 # release notes: https://github.com/traefik/traefik/releases networks: opencloud-net: diff --git a/devtools/deployments/shared/config/ldap/schemas/20_opencloud_education_schema.ldif b/devtools/deployments/shared/config/ldap/schemas/20_opencloud_education_schema.ldif index af29747456..8e5e5e9b70 100644 --- a/devtools/deployments/shared/config/ldap/schemas/20_opencloud_education_schema.ldif +++ b/devtools/deployments/shared/config/ldap/schemas/20_opencloud_education_schema.ldif @@ -42,4 +42,4 @@ olcObjectClasses: ( openCloudOid:1.2.5 NAME 'openCloudEducationSchool' DESC 'OpenCloud education school objectclass' SUP openCloudObject AUXILIARY - MAY ( openCloudEducationSchoolNumber $ openCloudEducationSchoolTerminationTimestamp ) ) + MAY ( openCloudEducationSchoolNumber $ openCloudEducationSchoolTerminationTimestamp $ openCloudEducationExternalId) ) diff --git a/go.mod b/go.mod index 5abfc95d40..1de5f25de6 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/onsi/gomega v1.39.1 github.com/open-policy-agent/opa v1.12.3 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 - github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 + github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068 github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8 github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 diff --git a/go.sum b/go.sum index 8e189d2c87..53e4fffab4 100644 --- a/go.sum +++ b/go.sum @@ -967,8 +967,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI= github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU= github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI= -github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI= -github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= +github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068 h1:i09YEVYbiUBMhxyak93REn/ZJOTRhAN4I3PXp2nCXgU= +github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8 h1:psi6BI08DZSxWRc+CxIU05kG1vQrpJ9Alv1U1d8HMLY= github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8/go.mod h1:EIhSI2Tv1FCdtvv5AO4ublX4yrM+1KZCcrkSImDVCDg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= diff --git a/services/graph/pkg/identity/ldap_education_class.go b/services/graph/pkg/identity/ldap_education_class.go index a45e01e238..f9c3d47cbc 100644 --- a/services/graph/pkg/identity/ldap_education_class.go +++ b/services/graph/pkg/identity/ldap_education_class.go @@ -7,8 +7,8 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/libregraph/idm/pkg/ldapdn" - "github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode" libregraph "github.com/opencloud-eu/libre-graph-api-go" + "github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode" ) type educationClassAttributeMap struct { diff --git a/services/graph/pkg/identity/ldap_education_school.go b/services/graph/pkg/identity/ldap_education_school.go index 519b309eb2..2aecc3d9bb 100644 --- a/services/graph/pkg/identity/ldap_education_school.go +++ b/services/graph/pkg/identity/ldap_education_school.go @@ -34,6 +34,7 @@ type educationConfig struct { type schoolAttributeMap struct { displayName string schoolNumber string + externalId string id string terminationDate string } @@ -48,9 +49,10 @@ const ( ) var ( - errNotSet = errors.New("attribute not set") - errSchoolNameExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that name is already present") - errSchoolNumberExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that number is already present") + errNotSet = errors.New("attribute not set") + errSchoolNameExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that name is already present") + errSchoolNumberExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that number is already present") + errSchoolExternalIdExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that external id is already present") ) func defaultEducationConfig() educationConfig { @@ -105,6 +107,7 @@ func newSchoolAttributeMap() schoolAttributeMap { return schoolAttributeMap{ displayName: "ou", schoolNumber: "openCloudEducationSchoolNumber", + externalId: "openCloudEducationExternalId", id: "openCloudUUID", terminationDate: "openCloudEducationSchoolTerminationTimestamp", } @@ -121,11 +124,11 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ // Check that the school number is not already used if school.HasSchoolNumber() { _, err := i.getSchoolByNumber(school.GetSchoolNumber()) - switch err { - case nil: + switch { + case err == nil: logger.Debug().Err(errSchoolNumberExists).Str("schoolNumber", school.GetSchoolNumber()).Msg("duplicate school number") return nil, errSchoolNumberExists - case ErrNotFound: + case errors.Is(err, ErrNotFound): break default: logger.Error().Err(err).Str("schoolNumber", school.GetSchoolNumber()).Msg("error looking up school by number") @@ -133,6 +136,21 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ } } + // Check that the school external id is not already used + if school.HasExternalId() { + _, err := i.getSchoolByExternalId(school.GetExternalId()) + switch { + case err == nil: + logger.Debug().Err(errSchoolExternalIdExists).Str("externalId", school.GetExternalId()).Msg("duplicate school external id") + return nil, errSchoolExternalIdExists + case errors.Is(err, ErrNotFound): + break + default: + logger.Error().Err(err).Str("externalId", school.GetExternalId()).Msg("error looking up school by external id") + return nil, errorcode.New(errorcode.GeneralException, "error looking up school by external id") + } + } + attributeTypeAndValue := ldap.AttributeTypeAndValue{ Type: i.educationConfig.schoolAttributeMap.displayName, Value: school.GetDisplayName(), @@ -147,6 +165,9 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ if school.HasSchoolNumber() { ar.Attribute(i.educationConfig.schoolAttributeMap.schoolNumber, []string{school.GetSchoolNumber()}) } + if school.HasExternalId() { + ar.Attribute(i.educationConfig.schoolAttributeMap.externalId, []string{school.GetExternalId()}) + } if !i.useServerUUID { ar.Attribute(i.educationConfig.schoolAttributeMap.id, []string{uuid.NewString()}) } @@ -278,14 +299,14 @@ func (i *LDAP) updateSchoolProperties(ctx context.Context, dn string, currentSch } // UpdateEducationSchool updates the supplied school in the identity backend -func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrID string, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) { +func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrIDOrExternalID string, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("UpdateEducationSchool") if !i.writeEnabled { return nil, ErrReadOnly } - e, err := i.getSchoolByNumberOrID(numberOrID) + e, err := i.getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID) if err != nil { return nil, err } @@ -308,7 +329,7 @@ func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrID string, sch } // Read back school from LDAP - e, err = i.getSchoolByNumberOrID(i.getID(e)) + e, err = i.getSchoolByNumberOrIDOrExternalID(i.getID(e)) if err != nil { return nil, err } @@ -322,7 +343,7 @@ func (i *LDAP) DeleteEducationSchool(ctx context.Context, id string) error { if !i.writeEnabled { return ErrReadOnly } - e, err := i.getSchoolByNumberOrID(id) + e, err := i.getSchoolByNumberOrIDOrExternalID(id) if err != nil { return err } @@ -337,10 +358,10 @@ func (i *LDAP) DeleteEducationSchool(ctx context.Context, id string) error { } // GetEducationSchool implements the EducationBackend interface for the LDAP backend. -func (i *LDAP) GetEducationSchool(ctx context.Context, numberOrID string) (*libregraph.EducationSchool, error) { +func (i *LDAP) GetEducationSchool(ctx context.Context, numberOrIDOrExternalID string) (*libregraph.EducationSchool, error) { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("GetEducationSchool") - e, err := i.getSchoolByNumberOrID(numberOrID) + e, err := i.getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID) if err != nil { return nil, err } @@ -415,11 +436,11 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, schoolNumberOrID str } // AddUsersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend. -func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error { +func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberIDs []string) error { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("AddUsersToEducationSchool") - schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID) + schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID) if err != nil { return err } @@ -462,11 +483,11 @@ func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrID s } // RemoveUserFromEducationSchool removes a single member (by ID) from a school -func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error { +func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberID string) error { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("RemoveUserFromEducationSchool") - schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID) + schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID) if err != nil { return err } @@ -521,12 +542,12 @@ func (i *LDAP) GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID s } func (i *LDAP) getEducationSchoolEntries( - schoolNumberOrID, filter, objectClass, baseDN string, + schoolNumberOrIDOrExternalID, filter, objectClass, baseDN string, scope int, attributes []string, logger log.Logger, ) ([]*ldap.Entry, error) { - schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID) + schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID) if err != nil { return nil, err } @@ -563,11 +584,11 @@ func (i *LDAP) getEducationSchoolEntries( } // 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 { +func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberIDs []string) error { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("AddClassesToEducationSchool") - schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID) + schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID) if err != nil { return err } @@ -610,11 +631,11 @@ func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID } // RemoveClassFromEducationSchool removes a single member (by ID) from a school -func (i *LDAP) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error { +func (i *LDAP) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberID string) error { logger := i.logger.SubloggerWithRequestID(ctx) logger.Debug().Str("backend", "ldap").Msg("RemoveClassFromEducationSchool") - schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID) + schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID) if err != nil { return err } @@ -652,14 +673,16 @@ func (i *LDAP) getSchoolByDN(dn string) (*ldap.Entry, error) { return i.getEntryByDN(dn, i.getEducationSchoolAttrTypes(), filter) } -func (i *LDAP) getSchoolByNumberOrID(numberOrID string) (*ldap.Entry, error) { - numberOrID = ldap.EscapeFilter(numberOrID) +func (i *LDAP) getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID string) (*ldap.Entry, error) { + numberOrIDOrExternalID = ldap.EscapeFilter(numberOrIDOrExternalID) filter := fmt.Sprintf( - "(|(%s=%s)(%s=%s))", + "(|(%s=%s)(%s=%s)(%s=%s))", i.educationConfig.schoolAttributeMap.id, - numberOrID, + numberOrIDOrExternalID, i.educationConfig.schoolAttributeMap.schoolNumber, - numberOrID, + numberOrIDOrExternalID, + i.educationConfig.schoolAttributeMap.externalId, + numberOrIDOrExternalID, ) return i.getSchoolByFilter(filter) } @@ -674,6 +697,16 @@ func (i *LDAP) getSchoolByNumber(schoolNumber string) (*ldap.Entry, error) { return i.getSchoolByFilter(filter) } +func (i *LDAP) getSchoolByExternalId(schoolExternalId string) (*ldap.Entry, error) { + schoolExternalId = ldap.EscapeFilter(schoolExternalId) + filter := fmt.Sprintf( + "(%s=%s)", + i.educationConfig.schoolAttributeMap.externalId, + schoolExternalId, + ) + return i.getSchoolByFilter(filter) +} + func (i *LDAP) getSchoolByFilter(filter string) (*ldap.Entry, error) { filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.educationConfig.schoolFilter, @@ -723,6 +756,8 @@ func (i *LDAP) createSchoolModelFromLDAP(e *ldap.Entry) *libregraph.EducationSch id := i.getID(e) schoolNumber := i.getSchoolNumber(e) + externalId := i.getExternalId(e) + t, err := i.getTerminationDate(e) if err != nil && !errors.Is(err, errNotSet) { i.logger.Error().Err(err).Str("dn", e.DN).Msg("Error reading termination date for LDAP entry") @@ -739,6 +774,9 @@ func (i *LDAP) createSchoolModelFromLDAP(e *ldap.Entry) *libregraph.EducationSch if schoolNumber != "" { school.SetSchoolNumber(schoolNumber) } + if externalId != "" { + school.SetExternalId(externalId) + } if t != nil { school.SetTerminationDate(*t) } @@ -750,6 +788,11 @@ func (i *LDAP) getSchoolNumber(e *ldap.Entry) string { return schoolNumber } +func (i *LDAP) getExternalId(e *ldap.Entry) string { + externalId := e.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.externalId) + return externalId +} + func (i *LDAP) getID(e *ldap.Entry) string { id := e.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id) return id diff --git a/services/graph/pkg/identity/ldap_education_school_test.go b/services/graph/pkg/identity/ldap_education_school_test.go index ac3137ae7d..5bc7718184 100644 --- a/services/graph/pkg/identity/ldap_education_school_test.go +++ b/services/graph/pkg/identity/ldap_education_school_test.go @@ -7,10 +7,10 @@ import ( "time" "github.com/go-ldap/ldap/v3" + libregraph "github.com/opencloud-eu/libre-graph-api-go" "github.com/opencloud-eu/opencloud/services/graph/pkg/config" "github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode" "github.com/opencloud-eu/opencloud/services/graph/pkg/identity/mocks" - libregraph "github.com/opencloud-eu/libre-graph-api-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -45,6 +45,7 @@ var schoolEntry = ldap.NewEntry("ou=Test School", "ou": {"Test School"}, "openCloudEducationSchoolNumber": {"0123"}, "openCloudUUID": {"abcd-defg"}, + "openCloudEducationExternalId": {"abcd-defg"}, }) var schoolEntry1 = ldap.NewEntry("ou=Test School1", @@ -52,20 +53,22 @@ var schoolEntry1 = ldap.NewEntry("ou=Test School1", "ou": {"Test School1"}, "openCloudEducationSchoolNumber": {"0042"}, "openCloudUUID": {"hijk-defg"}, + "openCloudEducationExternalId": {"hijk-defg"}, }) var schoolEntryWithTermination = ldap.NewEntry("ou=Test School", map[string][]string{ "ou": {"Test School"}, "openCloudEducationSchoolNumber": {"0123"}, "openCloudUUID": {"abcd-defg"}, + "openCloudEducationExternalId": {"abcd-defg"}, "openCloudEducationSchoolTerminationTimestamp": {"20420131120000Z"}, }) var ( - filterSchoolSearchByIdExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=abcd-defg)(openCloudEducationSchoolNumber=abcd-defg)))" - filterSchoolSearchByIdNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=xxxx-xxxx)(openCloudEducationSchoolNumber=xxxx-xxxx)))" - filterSchoolSearchByNumberExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=0123)(openCloudEducationSchoolNumber=0123)))" - filterSchoolSearchByNumberNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=3210)(openCloudEducationSchoolNumber=3210)))" + filterSchoolSearchByIdExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=abcd-defg)(openCloudEducationSchoolNumber=abcd-defg)(openCloudEducationExternalId=abcd-defg)))" + filterSchoolSearchByIdNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=xxxx-xxxx)(openCloudEducationSchoolNumber=xxxx-xxxx)(openCloudEducationExternalId=xxxx-xxxx)))" + filterSchoolSearchByNumberExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=0123)(openCloudEducationSchoolNumber=0123)(openCloudEducationExternalId=0123)))" + filterSchoolSearchByNumberNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=3210)(openCloudEducationSchoolNumber=3210)(openCloudEducationExternalId=3210)))" ) func TestCreateEducationSchool(t *testing.T) { diff --git a/vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go b/vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go index 89842fcd49..37ca4d2bf9 100644 --- a/vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go +++ b/vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go @@ -153,8 +153,19 @@ func (r ApiDeleteEducationUserRequest) Execute() (*http.Response, error) { /* DeleteEducationUser Delete educationUser +Deletes an education user by their internal ID. + +**To delete by external ID:** +If you only have an external ID, you must first retrieve the user's internal ID: +1. Call `GET /graph/v1.0/education/users?$filter=externalId eq '{value}'` +2. Extract the `id` from the response +3. Use that `id` in this DELETE endpoint + +See the [ListEducationUsers](#/educationUser/ListEducationUsers) operation for query details. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param userId key: id or username of user + @param userId key: internal user id (UUID format) or username of user. **Note:** If you only have an external ID, first query the user with `GET /graph/v1.0/education/users?$filter=externalId eq '{value}'` to retrieve the internal ID. @return ApiDeleteEducationUserRequest */ func (a *EducationUserApiService) DeleteEducationUser(ctx context.Context, userId string) ApiDeleteEducationUserRequest { @@ -360,10 +371,17 @@ func (a *EducationUserApiService) GetEducationUserExecute(r ApiGetEducationUserR type ApiListEducationUsersRequest struct { ctx context.Context ApiService *EducationUserApiService + filter *string orderby *[]string expand *[]string } +// Filter items by property values. Supports a subset of OData filter expressions. **Supported filters:** - By external ID: `externalId eq 'ext_12345'` +func (r ApiListEducationUsersRequest) Filter(filter string) ApiListEducationUsersRequest { + r.filter = &filter + return r +} + // Order items by property values func (r ApiListEducationUsersRequest) Orderby(orderby []string) ApiListEducationUsersRequest { r.orderby = &orderby @@ -383,6 +401,13 @@ func (r ApiListEducationUsersRequest) Execute() (*CollectionOfEducationUser, *ht /* ListEducationUsers Get entities from education users +Retrieves a collection of education users with optional filtering, ordering, and expansion. + +**Filtering by external ID:** +Use `$filter` to query users by their external identifier, for example: +`$filter=externalId eq 'EX12345'` + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @return ApiListEducationUsersRequest */ @@ -414,6 +439,9 @@ func (a *EducationUserApiService) ListEducationUsersExecute(r ApiListEducationUs localVarQueryParams := url.Values{} localVarFormParams := url.Values{} + if r.filter != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "$filter", r.filter, "form", "") + } if r.orderby != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "$orderby", r.orderby, "form", "csv") } diff --git a/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go b/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go index 7e145ec4ed..c3443debf7 100644 --- a/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go +++ b/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go @@ -26,6 +26,8 @@ type EducationSchool struct { DisplayName *string `json:"displayName,omitempty"` // School number SchoolNumber *string `json:"schoolNumber,omitempty"` + // External identifier of the school + ExternalId *string `json:"externalId,omitempty"` // Date and time at which the service for this organization is scheduled to be terminated TerminationDate NullableTime `json:"terminationDate,omitempty"` } @@ -143,6 +145,38 @@ func (o *EducationSchool) SetSchoolNumber(v string) { o.SchoolNumber = &v } +// GetExternalId returns the ExternalId field value if set, zero value otherwise. +func (o *EducationSchool) GetExternalId() string { + if o == nil || IsNil(o.ExternalId) { + var ret string + return ret + } + return *o.ExternalId +} + +// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *EducationSchool) GetExternalIdOk() (*string, bool) { + if o == nil || IsNil(o.ExternalId) { + return nil, false + } + return o.ExternalId, true +} + +// HasExternalId returns a boolean if a field has been set. +func (o *EducationSchool) HasExternalId() bool { + if o != nil && !IsNil(o.ExternalId) { + return true + } + + return false +} + +// SetExternalId gets a reference to the given string and assigns it to the ExternalId field. +func (o *EducationSchool) SetExternalId(v string) { + o.ExternalId = &v +} + // GetTerminationDate returns the TerminationDate field value if set, zero value otherwise (both if not set or set to explicit null). func (o *EducationSchool) GetTerminationDate() time.Time { if o == nil || IsNil(o.TerminationDate.Get()) { @@ -204,6 +238,9 @@ func (o EducationSchool) ToMap() (map[string]interface{}, error) { if !IsNil(o.SchoolNumber) { toSerialize["schoolNumber"] = o.SchoolNumber } + if !IsNil(o.ExternalId) { + toSerialize["externalId"] = o.ExternalId + } if o.TerminationDate.IsSet() { toSerialize["terminationDate"] = o.TerminationDate.Get() } diff --git a/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go b/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go index f0948b17f5..722212f252 100644 --- a/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go +++ b/vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go @@ -28,6 +28,8 @@ type EducationUser struct { // A collection of drives available for this user. Read-only. Drives []Drive `json:"drives,omitempty"` Drive *Drive `json:"drive,omitempty"` + // An external unique ID for the user. Use it to associate a user in another system, such as a student or employee ID number. + ExternalId *string `json:"externalId,omitempty"` // Identities associated with this account. Identities []ObjectIdentity `json:"identities,omitempty"` // The SMTP address for the user, for example, 'jeff@contoso.opencloud.com'. Returned by default. @@ -224,6 +226,38 @@ func (o *EducationUser) SetDrive(v Drive) { o.Drive = &v } +// GetExternalId returns the ExternalId field value if set, zero value otherwise. +func (o *EducationUser) GetExternalId() string { + if o == nil || IsNil(o.ExternalId) { + var ret string + return ret + } + return *o.ExternalId +} + +// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *EducationUser) GetExternalIdOk() (*string, bool) { + if o == nil || IsNil(o.ExternalId) { + return nil, false + } + return o.ExternalId, true +} + +// HasExternalId returns a boolean if a field has been set. +func (o *EducationUser) HasExternalId() bool { + if o != nil && !IsNil(o.ExternalId) { + return true + } + + return false +} + +// SetExternalId gets a reference to the given string and assigns it to the ExternalId field. +func (o *EducationUser) SetExternalId(v string) { + o.ExternalId = &v +} + // GetIdentities returns the Identities field value if set, zero value otherwise. func (o *EducationUser) GetIdentities() []ObjectIdentity { if o == nil || IsNil(o.Identities) { @@ -537,6 +571,9 @@ func (o EducationUser) ToMap() (map[string]interface{}, error) { if !IsNil(o.Drive) { toSerialize["drive"] = o.Drive } + if !IsNil(o.ExternalId) { + toSerialize["externalId"] = o.ExternalId + } if !IsNil(o.Identities) { toSerialize["identities"] = o.Identities } diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c5742b841..7c732fd3dc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1373,7 +1373,7 @@ github.com/open-policy-agent/opa/v1/version # github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 ## explicit; go 1.24.6 github.com/opencloud-eu/icap-client -# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 +# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068 ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go # github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8