From 2d29258caf954ba5ebe85ee9accb4a922eb05f68 Mon Sep 17 00:00:00 2001 From: Danny Date: Fri, 21 Oct 2022 18:36:56 -0400 Subject: [PATCH] GC: User Validation Feature (#1261) ## Description verifyBackupInputs() is a newly created function that ensures that a selector given to GraphConnector is valid to continue. Test src file: `graph_connector_test.go` expanded. ## Type of change - [x] :sunflower: Feature - [x] :bug: Bugfix ## Issue(s) *closes #1258 ## Test Plan - [x] :zap: Unit test --- src/internal/connector/graph_connector.go | 53 ++++++++++++++++ .../graph_connector_disconnected_test.go | 61 +++++++++++++++++++ .../connector/graph_connector_test.go | 38 ++++++++++++ 3 files changed, 152 insertions(+) diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index 7c57f0606..c6f1f8eb8 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "runtime/trace" + "strings" "sync" "github.com/hashicorp/go-multierror" @@ -377,9 +378,15 @@ func IsNonRecoverableError(e error) bool { return errors.As(e, &nonRecoverable) } +// DataCollections utility function to launch backup operations for exchange and onedrive func (gc *GraphConnector) DataCollections(ctx context.Context, sels selectors.Selector) ([]data.Collection, error) { defer trace.StartRegion(ctx, "gc:dataCollections:"+sels.Service.String()).End() + err := verifyBackupInputs(sels, gc.Users) + if err != nil { + return nil, err + } + switch sels.Service { case selectors.ServiceExchange: return gc.ExchangeDataCollection(ctx, sels) @@ -433,3 +440,49 @@ func (gc *GraphConnector) OneDriveDataCollections( return collections, errs } + +func verifyBackupInputs(sel selectors.Selector, mapOfUsers map[string]string) error { + var personnel []string + + // retrieve users from selectors + switch sel.Service { + case selectors.ServiceExchange: + backup, err := sel.ToExchangeBackup() + if err != nil { + return err + } + + for _, scope := range backup.Scopes() { + temp := scope.Get(selectors.ExchangeUser) + personnel = append(personnel, temp...) + } + case selectors.ServiceOneDrive: + backup, err := sel.ToOneDriveBackup() + if err != nil { + return err + } + + for _, user := range backup.Scopes() { + temp := user.Get(selectors.OneDriveUser) + personnel = append(personnel, temp...) + } + + default: + return errors.New("service %s not supported") + } + + // verify personnel + normUsers := map[string]struct{}{} + + for k := range mapOfUsers { + normUsers[strings.ToLower(k)] = struct{}{} + } + + for _, user := range personnel { + if _, ok := normUsers[strings.ToLower(user)]; !ok { + return fmt.Errorf("%s user not found within tenant", user) + } + } + + return nil +} diff --git a/src/internal/connector/graph_connector_disconnected_test.go b/src/internal/connector/graph_connector_disconnected_test.go index 8e9030e37..7f6ee0a00 100644 --- a/src/internal/connector/graph_connector_disconnected_test.go +++ b/src/internal/connector/graph_connector_disconnected_test.go @@ -204,3 +204,64 @@ func (suite *DisconnectedGraphConnectorSuite) TestRestoreFailsBadService() { assert.Equal(t, 0, status.FolderCount) assert.Equal(t, 0, status.Successful) } + +func (suite *DisconnectedGraphConnectorSuite) TestVerifyBackupInputs() { + users := make(map[string]string) + users["elliotReid@someHospital.org"] = "" + users["chrisTurk@someHospital.org"] = "" + users["carlaEspinosa@someHospital.org"] = "" + users["bobKelso@someHospital.org"] = "" + users["johnDorian@someHospital.org"] = "" + + tests := []struct { + name string + getSelector func(t *testing.T) selectors.Selector + checkError assert.ErrorAssertionFunc + }{ + { + name: "Invalid User", + checkError: assert.Error, + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewOneDriveBackup() + sel.Include(sel.Folders([]string{"foo@SomeCompany.org"}, selectors.Any())) + return sel.Selector + }, + }, + { + name: "Valid Single User", + checkError: assert.NoError, + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.MailFolders([]string{"bobkelso@someHospital.org"}, selectors.Any())) + return sel.Selector + }, + }, + { + name: "Partial invalid user", + checkError: assert.Error, + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.MailFolders([]string{"bobkelso@someHospital.org", "janitor@someHospital.org"}, selectors.Any())) + return sel.Selector + }, + }, + { + name: "Multiple Valid Users", + checkError: assert.NoError, + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewOneDriveBackup() + sel.Include( + sel.Users([]string{"elliotReid@someHospital.org", "johnDorian@someHospital.org", "christurk@somehospital.org"})) + + return sel.Selector + }, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + err := verifyBackupInputs(test.getSelector(t), users) + test.checkError(t, err) + }) + } +} diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 0b094a97a..6f8480bef 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -79,6 +79,44 @@ func (suite *GraphConnectorIntegrationSuite) TestSetTenantUsers() { suite.Greater(len(newConnector.Users), 0) } +// TestInvalidUserForDataCollections ensures verification process for users +func (suite *GraphConnectorIntegrationSuite) TestInvalidUserForDataCollections() { + ctx, flush := tester.NewContext() + defer flush() + + invalidUser := "foo@example.com" + connector := loadConnector(ctx, suite.T()) + tests := []struct { + name string + getSelector func(t *testing.T) selectors.Selector + }{ + { + name: "invalid exchange backup user", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.MailFolders([]string{invalidUser}, selectors.Any())) + return sel.Selector + }, + }, + { + name: "Invalid onedrive backup user", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewOneDriveBackup() + sel.Include(sel.Folders([]string{invalidUser}, selectors.Any())) + return sel.Selector + }, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + collections, err := connector.DataCollections(ctx, test.getSelector(t)) + assert.Error(t, err) + assert.Empty(t, collections) + }) + } +} + // TestExchangeDataCollection verifies interface between operation and // GraphConnector remains stable to receive a non-zero amount of Collections // for the Exchange Package. Enabled exchange applications: