diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index cea7b684b..527e13765 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -272,7 +272,7 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error { sel := exchangeBackupCreateSelectors(user, exchangeData) - users, err := m365.UserIDs(ctx, acct) + users, err := m365.UserPNs(ctx, acct) if err != nil { return Only(ctx, errors.Wrap(err, "Failed to retrieve M365 users")) } diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index a7b13f783..5b602d8ee 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -194,7 +194,7 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error { sel := oneDriveBackupCreateSelectors(user) - users, err := m365.UserIDs(ctx, acct) + users, err := m365.UserPNs(ctx, acct) if err != nil { return Only(ctx, errors.Wrap(err, "Failed to retrieve M365 users")) } diff --git a/src/internal/connector/data_collections.go b/src/internal/connector/data_collections.go index 4f4cba280..1ca1f73a3 100644 --- a/src/internal/connector/data_collections.go +++ b/src/internal/connector/data_collections.go @@ -98,8 +98,6 @@ func (gc *GraphConnector) DataCollections( func verifyBackupInputs(sels selectors.Selector, userPNs, siteIDs []string) error { var ids []string - resourceOwners := sels.DiscreteResourceOwners() - switch sels.Service { case selectors.ServiceExchange, selectors.ServiceOneDrive: ids = userPNs @@ -115,10 +113,8 @@ func verifyBackupInputs(sels selectors.Selector, userPNs, siteIDs []string) erro normROs[strings.ToLower(id)] = struct{}{} } - for _, ro := range resourceOwners { - if _, ok := normROs[strings.ToLower(ro)]; !ok { - return fmt.Errorf("included resource owner %s not found within tenant", ro) - } + if _, ok := normROs[strings.ToLower(sels.DiscreteOwner)]; !ok { + return fmt.Errorf("resource owner [%s] not found within tenant", sels.DiscreteOwner) } return nil diff --git a/src/internal/connector/data_collections_test.go b/src/internal/connector/data_collections_test.go index c4f005e3c..60dcaf64c 100644 --- a/src/internal/connector/data_collections_test.go +++ b/src/internal/connector/data_collections_test.go @@ -131,12 +131,11 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection } // TestInvalidUserForDataCollections ensures verification process for users -func (suite *ConnectorDataCollectionIntegrationSuite) TestInvalidUserForDataCollections() { +func (suite *ConnectorDataCollectionIntegrationSuite) TestDataCollections_invalidResourceOwner() { ctx, flush := tester.NewContext() defer flush() - invalidUser := "foo@example.com" - selUsers := []string{invalidUser} + owners := []string{"snuffleupagus"} connector := loadConnector(ctx, suite.T(), Users) tests := []struct { @@ -146,16 +145,51 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestInvalidUserForDataColl { name: "invalid exchange backup user", getSelector: func(t *testing.T) selectors.Selector { - sel := selectors.NewExchangeBackup(selUsers) - sel.Include(sel.MailFolders(selUsers, selectors.Any())) + sel := selectors.NewExchangeBackup(owners) + sel.Include(sel.MailFolders(owners, selectors.Any())) return sel.Selector }, }, { name: "Invalid onedrive backup user", getSelector: func(t *testing.T) selectors.Selector { - sel := selectors.NewOneDriveBackup(selUsers) - sel.Include(sel.Folders(selUsers, selectors.Any())) + sel := selectors.NewOneDriveBackup(owners) + sel.Include(sel.Folders(owners, selectors.Any())) + return sel.Selector + }, + }, + { + name: "Invalid sharepoint backup site", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewSharePointBackup(owners) + sel.Include(sel.Libraries(owners, selectors.Any())) + return sel.Selector + }, + }, + { + name: "missing exchange backup user", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup(owners) + sel.Include(sel.MailFolders(owners, selectors.Any())) + sel.DiscreteOwner = "" + return sel.Selector + }, + }, + { + name: "missing onedrive backup user", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewOneDriveBackup(owners) + sel.Include(sel.Folders(owners, selectors.Any())) + sel.DiscreteOwner = "" + return sel.Selector + }, + }, + { + name: "missing sharepoint backup site", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewSharePointBackup(owners) + sel.Include(sel.Libraries(owners, selectors.Any())) + sel.DiscreteOwner = "" return sel.Selector }, }, diff --git a/src/internal/connector/graph_connector_disconnected_test.go b/src/internal/connector/graph_connector_disconnected_test.go index f16846acc..218a49290 100644 --- a/src/internal/connector/graph_connector_disconnected_test.go +++ b/src/internal/connector/graph_connector_disconnected_test.go @@ -227,19 +227,7 @@ func (suite *DisconnectedGraphConnectorSuite) TestVerifyBackupInputs() { getSelector: func(t *testing.T) selectors.Selector { sel := selectors.NewExchangeBackup([]string{"bobkelso@someHospital.org", "janitor@someHospital.org"}) 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( - []string{"elliotReid@someHospital.org", "johnDorian@someHospital.org", "christurk@somehospital.org"}, - ) - sel.Include( - sel.Users([]string{"elliotReid@someHospital.org", "johnDorian@someHospital.org", "christurk@somehospital.org"})) - + sel.DiscreteOwner = "janitor@someHospital.org" return sel.Selector }, }, @@ -268,18 +256,21 @@ func (suite *DisconnectedGraphConnectorSuite) TestVerifyBackupInputs_allServices name: "Valid User", checkError: assert.NoError, excludes: func(t *testing.T) selectors.Selector { - sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org"}) + sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org", "foo@SomeCompany.org"}) sel.Exclude(sel.Folders([]string{"elliotReid@someHospital.org"}, selectors.Any())) + sel.DiscreteOwner = "elliotReid@someHospital.org" return sel.Selector }, filters: func(t *testing.T) selectors.Selector { - sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org"}) + sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org", "foo@SomeCompany.org"}) sel.Filter(sel.Folders([]string{"elliotReid@someHospital.org"}, selectors.Any())) + sel.DiscreteOwner = "elliotReid@someHospital.org" return sel.Selector }, includes: func(t *testing.T) selectors.Selector { - sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org"}) + sel := selectors.NewOneDriveBackup([]string{"elliotReid@someHospital.org", "foo@SomeCompany.org"}) sel.Include(sel.Folders([]string{"elliotReid@someHospital.org"}, selectors.Any())) + sel.DiscreteOwner = "elliotReid@someHospital.org" return sel.Selector }, }, @@ -308,16 +299,19 @@ func (suite *DisconnectedGraphConnectorSuite) TestVerifyBackupInputs_allServices excludes: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"abc.site.foo", "bar.site.baz"}) sel.Exclude(sel.Sites([]string{"abc.site.foo", "bar.site.baz"})) + sel.DiscreteOwner = "abc.site.foo" return sel.Selector }, filters: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"abc.site.foo", "bar.site.baz"}) sel.Filter(sel.Sites([]string{"abc.site.foo", "bar.site.baz"})) + sel.DiscreteOwner = "abc.site.foo" return sel.Selector }, includes: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"abc.site.foo", "bar.site.baz"}) sel.Include(sel.Sites([]string{"abc.site.foo", "bar.site.baz"})) + sel.DiscreteOwner = "abc.site.foo" return sel.Selector }, }, @@ -327,16 +321,19 @@ func (suite *DisconnectedGraphConnectorSuite) TestVerifyBackupInputs_allServices excludes: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"fnords.smarfs.brawnhilda"}) sel.Exclude(sel.Sites([]string{"fnords.smarfs.brawnhilda"})) + sel.DiscreteOwner = "fnords.smarfs.brawnhilda" return sel.Selector }, filters: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"fnords.smarfs.brawnhilda"}) sel.Filter(sel.Sites([]string{"fnords.smarfs.brawnhilda"})) + sel.DiscreteOwner = "fnords.smarfs.brawnhilda" return sel.Selector }, includes: func(t *testing.T) selectors.Selector { sel := selectors.NewSharePointBackup([]string{"fnords.smarfs.brawnhilda"}) sel.Include(sel.Sites([]string{"fnords.smarfs.brawnhilda"})) + sel.DiscreteOwner = "fnords.smarfs.brawnhilda" return sel.Selector }, }, diff --git a/src/pkg/services/m365/m365.go b/src/pkg/services/m365/m365.go index 0bc2dbf9a..19da0a21f 100644 --- a/src/pkg/services/m365/m365.go +++ b/src/pkg/services/m365/m365.go @@ -58,6 +58,22 @@ func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error) return ret, nil } +// UserPNs retrieves all user principleNames in the tenant. Principle Names +// can be used analogous userIDs in graph API queries. +func UserPNs(ctx context.Context, m365Account account.Account) ([]string, error) { + users, err := Users(ctx, m365Account) + if err != nil { + return nil, err + } + + ret := make([]string, 0, len(users)) + for _, u := range users { + ret = append(ret, u.PrincipalName) + } + + return ret, nil +} + // SiteURLs returns a list of SharePoint site WebURLs in the specified M365 tenant func SiteURLs(ctx context.Context, m365Account account.Account) ([]string, error) { gc, err := connector.NewGraphConnector(ctx, m365Account, connector.Sites)