From f30c8362850bb254afdd0d2c52c171b15a62dc44 Mon Sep 17 00:00:00 2001 From: Jumpy Squirrel Date: Tue, 17 Dec 2024 17:57:42 +0100 Subject: [PATCH] feat(#246): allow searching for a small list of identity_subjects --- api/openapi-spec/openapi.yaml | 15 ++++++++++++++ internal/api/v1/attendee/api.go | 1 + .../repository/database/inmemorydb/match.go | 11 +++++++++- .../database/mysqldb/searchquery.go | 20 +++++++++++++++++++ .../database/mysqldb/searchquery_test.go | 8 ++++++-- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/api/openapi-spec/openapi.yaml b/api/openapi-spec/openapi.yaml index dd17ca9..2ab22fe 100644 --- a/api/openapi-spec/openapi.yaml +++ b/api/openapi-spec/openapi.yaml @@ -2231,6 +2231,21 @@ components: sponsor-items: 1 key-hand-out: 0 overdue: 1 + identity_subjects: + description: |- + exact match to search for a set of OIDC subjects as cached in registrations. No condition if left empty. + + IMPORTANT: Use of this field is limited to 8 identities, any additional identities in you list will be silently ignored! + If you need to check a large number of identities, please request the identity_subject search result field, + and filter on your end! + + Note that caching of identities is configuration dependent, if disabled, no registrations will ever match if this is set. + type: array + maxItems: 8 + items: + type: string + example: + - '1234567890' ChoiceStateCondition: type: integer format: int64 diff --git a/internal/api/v1/attendee/api.go b/internal/api/v1/attendee/api.go index d6eed61..f7148c3 100644 --- a/internal/api/v1/attendee/api.go +++ b/internal/api/v1/attendee/api.go @@ -84,6 +84,7 @@ type AttendeeSearchSingleCriterion struct { Permissions map[string]int8 `json:"permissions"` AdminComments string `json:"admin_comments"` AddInfo map[string]int8 `json:"add_info"` // can only search for presence of a value for each area, Note: special area 'overdue' + IdentitySubjects []string `json:"identity_subjects"` } // --- search result --- diff --git a/internal/repository/database/inmemorydb/match.go b/internal/repository/database/inmemorydb/match.go index 1b52a16..96cd2b4 100644 --- a/internal/repository/database/inmemorydb/match.go +++ b/internal/repository/database/inmemorydb/match.go @@ -8,6 +8,7 @@ import ( "github.com/eurofurence/reg-attendee-service/internal/repository/config" "github.com/eurofurence/reg-attendee-service/internal/web/util/validation" "github.com/ryanuber/go-glob" + "slices" "strconv" "strings" ) @@ -48,7 +49,8 @@ func (r *InMemoryRepository) matches(cond *attendee.AttendeeSearchSingleCriterio matchesSubstringGlobOrEmpty(cond.AdminComments, adm.AdminComments) && matchesAddInfoPresence(cond.AddInfo, addInf) && matchesOverdue(cond.AddInfo, a.CacheDueDate, r.Now().Format(config.IsoDateFormat), st.Status) && - matchesIsoDateRange(cond.BirthdayFrom, cond.BirthdayTo, a.Birthday) + matchesIsoDateRange(cond.BirthdayFrom, cond.BirthdayTo, a.Birthday) && + matchesIdentitySubjects(cond.IdentitySubjects, a.Identity) } func matchesUintSliceOrEmpty(cond []uint, value uint) bool { @@ -183,3 +185,10 @@ func matchesIsoDateRange(condFrom string, condTo string, value string) bool { } return true } + +func matchesIdentitySubjects(cond []string, value string) bool { + if len(cond) > 8 { + cond = cond[:8] + } + return len(cond) == 0 || slices.Contains(cond, value) +} diff --git a/internal/repository/database/mysqldb/searchquery.go b/internal/repository/database/mysqldb/searchquery.go index 215184d..3bb8952 100644 --- a/internal/repository/database/mysqldb/searchquery.go +++ b/internal/repository/database/mysqldb/searchquery.go @@ -261,6 +261,9 @@ func (r *MysqlRepository) addSingleCondition(cond *attendee.AttendeeSearchSingle if cond.BirthdayTo != "" { query.WriteString(stringLessThanOrEqual("a.birthday", cond.BirthdayTo, params, paramBaseName, ¶mNo)) } + if len(cond.IdentitySubjects) > 0 { + query.WriteString(stringSliceMatch("a.identity", cond.IdentitySubjects, params, paramBaseName, ¶mNo)) + } return query.String() } @@ -315,6 +318,23 @@ func safeStatusSliceMatch(field string, values []status.Status) string { return fmt.Sprintf(" AND ( %s IN (%s) )\n", field, strings.Join(mappedValues, ",")) } +func stringSliceMatch(field string, values []string, params map[string]interface{}, paramBaseName string, idx *int) string { + var result strings.Builder + result.WriteString(fmt.Sprintf(" AND ( %s IN ( ", field)) + cond := make([]string, 0) + for i, v := range values { + if i < 8 { + pName := fmt.Sprintf("%s_%d_%d", paramBaseName, *idx, i+1) + params[pName] = v + cond = append(cond, "@"+pName) + } + } + *idx++ + result.WriteString(strings.Join(cond, " , ")) + result.WriteString(" ) )\n") + return result.String() +} + func substringMatch(field string, condition string, params map[string]interface{}, paramBaseName string, idx *int) string { return fullstringMatch(field, "*"+condition+"*", params, paramBaseName, idx) } diff --git a/internal/repository/database/mysqldb/searchquery_test.go b/internal/repository/database/mysqldb/searchquery_test.go index 2673dbc..e37f1a3 100644 --- a/internal/repository/database/mysqldb/searchquery_test.go +++ b/internal/repository/database/mysqldb/searchquery_test.go @@ -110,8 +110,9 @@ func TestTwoFullSearchQueries(t *testing.T) { "overdue": 1, "sponsor-items": 1, }, - BirthdayFrom: "1970-10-24", - BirthdayTo: "1980-12-24", + BirthdayFrom: "1970-10-24", + BirthdayTo: "1980-12-24", + IdentitySubjects: []string{"Q1E4D2", "R1E5DD"}, }, }, MinId: 1, @@ -158,6 +159,8 @@ func TestTwoFullSearchQueries(t *testing.T) { "param_2_1": "small%bird", "param_2_2": "Johnny", "param_2_20": "1980-12-24", + "param_2_21_1": "Q1E4D2", + "param_2_21_2": "R1E5DD", "param_2_3": "%Berlin%", "param_2_4": "CH", "param_2_5": "%gg@hh%", @@ -238,6 +241,7 @@ WHERE ( AND ( ( SELECT COUNT(*) FROM att_additional_infos WHERE attendee_id = a.id AND area = @param_2_18_2 ) > 0 ) AND ( STRCMP(a.birthday,@param_2_19) >= 0 ) AND ( STRCMP(a.birthday,@param_2_20) <= 0 ) + AND ( a.identity IN ( @param_2_21_1 , @param_2_21_2 ) ) ) ) AND a.id >= @param_0_1 AND a.id <= @param_0_2 ORDER BY CONCAT(a.first_name, ' ', a.last_name) DESC`