From 666813865e21cddea854969ec51207512e9a9749 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 16 May 2023 12:15:18 -0600 Subject: [PATCH] case sensitive selection for item IDs (#3397) Use a strictEquals matcher for item selection when not matching on item names (no name match assumes canonical system id match, which is case sensitive). --- #### Does this PR need a docs update or release note? - [x] :no_entry: No #### Type of change - [x] :bug: Bugfix #### Issue(s) * #3313 #### Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/pkg/selectors/exchange.go | 27 +++++--------- src/pkg/selectors/helpers_test.go | 8 ++++ src/pkg/selectors/onedrive.go | 8 ++-- src/pkg/selectors/scopes_test.go | 61 +++++++++++++++++++++++++++++++ src/pkg/selectors/selectors.go | 38 ++++++++++++++++--- src/pkg/selectors/sharepoint.go | 19 ++++------ 6 files changed, 121 insertions(+), 40 deletions(-) diff --git a/src/pkg/selectors/exchange.go b/src/pkg/selectors/exchange.go index b5463f5d1..008134559 100644 --- a/src/pkg/selectors/exchange.go +++ b/src/pkg/selectors/exchange.go @@ -216,9 +216,8 @@ func (s *exchange) Contacts(folders, contacts []string, opts ...option) []Exchan scopes = append( scopes, - makeScope[ExchangeScope](ExchangeContact, contacts). - set(ExchangeContactFolder, folders, opts...), - ) + makeScope[ExchangeScope](ExchangeContact, contacts, defaultItemOptions(s.Cfg)...). + set(ExchangeContactFolder, folders, opts...)) return scopes } @@ -236,8 +235,7 @@ func (s *exchange) ContactFolders(folders []string, opts ...option) []ExchangeSc scopes = append( scopes, - makeScope[ExchangeScope](ExchangeContactFolder, folders, os...), - ) + makeScope[ExchangeScope](ExchangeContactFolder, folders, os...)) return scopes } @@ -252,9 +250,8 @@ func (s *exchange) Events(calendars, events []string, opts ...option) []Exchange scopes = append( scopes, - makeScope[ExchangeScope](ExchangeEvent, events). - set(ExchangeEventCalendar, calendars, opts...), - ) + makeScope[ExchangeScope](ExchangeEvent, events, defaultItemOptions(s.Cfg)...). + set(ExchangeEventCalendar, calendars, opts...)) return scopes } @@ -273,8 +270,7 @@ func (s *exchange) EventCalendars(events []string, opts ...option) []ExchangeSco scopes = append( scopes, - makeScope[ExchangeScope](ExchangeEventCalendar, events, os...), - ) + makeScope[ExchangeScope](ExchangeEventCalendar, events, os...)) return scopes } @@ -289,9 +285,8 @@ func (s *exchange) Mails(folders, mails []string, opts ...option) []ExchangeScop scopes = append( scopes, - makeScope[ExchangeScope](ExchangeMail, mails). - set(ExchangeMailFolder, folders, opts...), - ) + makeScope[ExchangeScope](ExchangeMail, mails, defaultItemOptions(s.Cfg)...). + set(ExchangeMailFolder, folders, opts...)) return scopes } @@ -309,8 +304,7 @@ func (s *exchange) MailFolders(folders []string, opts ...option) []ExchangeScope scopes = append( scopes, - makeScope[ExchangeScope](ExchangeMailFolder, folders, os...), - ) + makeScope[ExchangeScope](ExchangeMailFolder, folders, os...)) return scopes } @@ -326,8 +320,7 @@ func (s *exchange) AllData() []ExchangeScope { scopes = append(scopes, makeScope[ExchangeScope](ExchangeContactFolder, Any()), makeScope[ExchangeScope](ExchangeEventCalendar, Any()), - makeScope[ExchangeScope](ExchangeMailFolder, Any()), - ) + makeScope[ExchangeScope](ExchangeMailFolder, Any())) return scopes } diff --git a/src/pkg/selectors/helpers_test.go b/src/pkg/selectors/helpers_test.go index 28a4ba34d..82e68791e 100644 --- a/src/pkg/selectors/helpers_test.go +++ b/src/pkg/selectors/helpers_test.go @@ -146,6 +146,14 @@ func stubInfoScope(match string) mockScope { return sc } +func makeStubScope(cfg Config, match []string) mockScope { + return makeScope[mockScope](leafCatStub, match, defaultItemOptions(cfg)...) +} + +func (s mockScope) Matches(cat mockCategorizer, target string) bool { + return matches(s, cat, target) +} + // --------------------------------------------------------------------------- // Stringers and Concealers // --------------------------------------------------------------------------- diff --git a/src/pkg/selectors/onedrive.go b/src/pkg/selectors/onedrive.go index 7d4661199..18fa0fca3 100644 --- a/src/pkg/selectors/onedrive.go +++ b/src/pkg/selectors/onedrive.go @@ -223,8 +223,7 @@ func (s *oneDrive) Folders(folders []string, opts ...option) []OneDriveScope { scopes = append( scopes, - makeScope[OneDriveScope](OneDriveFolder, folders, os...), - ) + makeScope[OneDriveScope](OneDriveFolder, folders, os...)) return scopes } @@ -239,9 +238,8 @@ func (s *oneDrive) Items(folders, items []string, opts ...option) []OneDriveScop scopes = append( scopes, - makeScope[OneDriveScope](OneDriveItem, items). - set(OneDriveFolder, folders, opts...), - ) + makeScope[OneDriveScope](OneDriveItem, items, defaultItemOptions(s.Cfg)...). + set(OneDriveFolder, folders, opts...)) return scopes } diff --git a/src/pkg/selectors/scopes_test.go b/src/pkg/selectors/scopes_test.go index a70de3806..92af1a572 100644 --- a/src/pkg/selectors/scopes_test.go +++ b/src/pkg/selectors/scopes_test.go @@ -460,6 +460,67 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() { } } +func (suite *SelectorScopesSuite) TestDefaultItemOptions() { + table := []struct { + name string + cfg Config + match []string + target string + expect assert.BoolAssertionFunc + }{ + { + name: "no config, matches same value", + cfg: Config{}, + match: []string{"foo"}, + target: "foo", + expect: assert.True, + }, + { + name: "no config, does not match different case", + cfg: Config{}, + match: []string{"bar"}, + target: "BAR", + expect: assert.False, + }, + { + name: "no config, does not match substring", + cfg: Config{}, + match: []string{"bar"}, + target: "ba", + expect: assert.False, + }, + { + name: "only names, matches same same value", + cfg: Config{OnlyMatchItemNames: true}, + match: []string{"fnords"}, + target: "fnords", + expect: assert.True, + }, + { + name: "only names, matches different case", + cfg: Config{OnlyMatchItemNames: true}, + match: []string{"smarf"}, + target: "SMARF", + expect: assert.True, + }, + { + name: "only names, does not match substring", + cfg: Config{OnlyMatchItemNames: true}, + match: []string{"brunhilda"}, + target: "unhild", + expect: assert.False, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + sc := makeStubScope(test.cfg, test.match) + + test.expect(t, sc.Matches(leafCatStub, test.target)) + }) + } +} + func (suite *SelectorScopesSuite) TestClean() { table := []struct { name string diff --git a/src/pkg/selectors/selectors.go b/src/pkg/selectors/selectors.go index 5f614705c..86a1cb56c 100644 --- a/src/pkg/selectors/selectors.go +++ b/src/pkg/selectors/selectors.go @@ -463,11 +463,26 @@ func pathCategoriesIn[T scopeT, C categoryT](ss []scope) []path.CategoryType { // scope constructors // --------------------------------------------------------------------------- +// constructs the default item-scope comparator options according +// to the selector configuration. +// - if cfg.OnlyMatchItemNames == false, then comparison assumes item IDs, +// which are case sensitive, resulting in StrictEqualsMatch +func defaultItemOptions(cfg Config) []option { + opts := []option{} + + if !cfg.OnlyMatchItemNames { + opts = append(opts, StrictEqualMatch()) + } + + return opts +} + type scopeConfig struct { - usePathFilter bool - usePrefixFilter bool - useSuffixFilter bool - useEqualsFilter bool + usePathFilter bool + usePrefixFilter bool + useSuffixFilter bool + useEqualsFilter bool + useStrictEqualsFilter bool } type option func(*scopeConfig) @@ -496,9 +511,16 @@ func SuffixMatch() option { } } +// StrictEqualsMatch ensures the selector uses a StrictEquals comparator, instead +// of contains. Will not override a default Any() or None() comparator. +func StrictEqualMatch() option { + return func(sc *scopeConfig) { + sc.useStrictEqualsFilter = true + } +} + // ExactMatch ensures the selector uses an Equals comparator, instead -// of contains. Will not override a default Any() or None() -// comparator. +// of contains. Will not override a default Any() or None() comparator. func ExactMatch() option { return func(sc *scopeConfig) { sc.useEqualsFilter = true @@ -599,6 +621,10 @@ func filterize( return filters.Suffix(targets) } + if sc.useStrictEqualsFilter { + return filters.StrictEqual(targets) + } + if defaultFilter != nil { return defaultFilter(targets) } diff --git a/src/pkg/selectors/sharepoint.go b/src/pkg/selectors/sharepoint.go index 55b5d4807..a408f6339 100644 --- a/src/pkg/selectors/sharepoint.go +++ b/src/pkg/selectors/sharepoint.go @@ -245,8 +245,7 @@ func (s *sharePoint) AllData() []SharePointScope { scopes, makeScope[SharePointScope](SharePointLibraryFolder, Any()), makeScope[SharePointScope](SharePointList, Any()), - makeScope[SharePointScope](SharePointPageFolder, Any()), - ) + makeScope[SharePointScope](SharePointPageFolder, Any())) return scopes } @@ -276,9 +275,8 @@ func (s *sharePoint) ListItems(lists, items []string, opts ...option) []SharePoi scopes = append( scopes, - makeScope[SharePointScope](SharePointListItem, items). - set(SharePointList, lists, opts...), - ) + makeScope[SharePointScope](SharePointListItem, items, defaultItemOptions(s.Cfg)...). + set(SharePointList, lists, opts...)) return scopes } @@ -312,8 +310,7 @@ func (s *sharePoint) LibraryFolders(libraryFolders []string, opts ...option) []S scopes = append( scopes, - makeScope[SharePointScope](SharePointLibraryFolder, libraryFolders, os...), - ) + makeScope[SharePointScope](SharePointLibraryFolder, libraryFolders, os...)) return scopes } @@ -328,9 +325,8 @@ func (s *sharePoint) LibraryItems(libraries, items []string, opts ...option) []S scopes = append( scopes, - makeScope[SharePointScope](SharePointLibraryItem, items). - set(SharePointLibraryFolder, libraries, opts...), - ) + makeScope[SharePointScope](SharePointLibraryItem, items, defaultItemOptions(s.Cfg)...). + set(SharePointLibraryFolder, libraries, opts...)) return scopes } @@ -361,8 +357,7 @@ func (s *sharePoint) PageItems(pages, items []string, opts ...option) []SharePoi scopes = append( scopes, makeScope[SharePointScope](SharePointPage, items). - set(SharePointPageFolder, pages, opts...), - ) + set(SharePointPageFolder, pages, opts...)) return scopes }