Integration test for permissions changes (#2965)

This adds basic integration tests for backing up permissions in OneDrive.
*https://github.com/alcionai/corso/issues/2790 could be a good follow up to this.*

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* fixes https://github.com/alcionai/corso/issues/2772

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [ ]  Unit test
- [x] 💚 E2E
This commit is contained in:
Abin Simon 2023-03-29 10:15:25 +05:30 committed by GitHub
parent dde61db779
commit 6b00548a0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 68 deletions

View File

@ -8,7 +8,6 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/internal/version"
@ -93,7 +92,6 @@ func createRestoreFoldersWithPermissions(
drivePath *path.DrivePath, drivePath *path.DrivePath,
restoreFolders []string, restoreFolders []string,
folderMetadata Metadata, folderMetadata Metadata,
permissionIDMappings map[string]string,
) (string, error) { ) (string, error) {
id, err := CreateRestoreFolders(ctx, service, drivePath.DriveID, restoreFolders) id, err := CreateRestoreFolders(ctx, service, drivePath.DriveID, restoreFolders)
if err != nil { if err != nil {
@ -105,13 +103,12 @@ func createRestoreFoldersWithPermissions(
return id, nil return id, nil
} }
err = restorePermissions( err = RestorePermissions(
ctx, ctx,
service, service,
drivePath.DriveID, drivePath.DriveID,
id, id,
folderMetadata, folderMetadata)
permissionIDMappings)
return id, err return id, err
} }
@ -126,7 +123,6 @@ func isSame(first, second []string) bool {
func diffPermissions( func diffPermissions(
before, after []UserPermission, before, after []UserPermission,
permissionIDMappings map[string]string,
) ([]UserPermission, []UserPermission) { ) ([]UserPermission, []UserPermission) {
var ( var (
added = []UserPermission{} added = []UserPermission{}
@ -168,16 +164,15 @@ func diffPermissions(
return added, removed return added, removed
} }
// restorePermissions takes in the permissions that were added and the // RestorePermissions takes in the permissions that were added and the
// removed(ones present in parent but not in child) and adds/removes // removed(ones present in parent but not in child) and adds/removes
// the necessary permissions on onedrive objects. // the necessary permissions on onedrive objects.
func restorePermissions( func RestorePermissions(
ctx context.Context, ctx context.Context,
service graph.Servicer, service graph.Servicer,
driveID string, driveID string,
itemID string, itemID string,
meta Metadata, meta Metadata,
permissionIDMappings map[string]string,
) error { ) error {
if meta.SharingMode == SharingModeInherited { if meta.SharingMode == SharingModeInherited {
return nil return nil
@ -191,7 +186,7 @@ func restorePermissions(
return graph.Wrap(ctx, err, "fetching current permissions") return graph.Wrap(ctx, err, "fetching current permissions")
} }
permAdded, permRemoved := diffPermissions(currentPermissions, meta.Permissions, permissionIDMappings) permAdded, permRemoved := diffPermissions(currentPermissions, meta.Permissions)
for _, p := range permRemoved { for _, p := range permRemoved {
err := service.Client(). err := service.Client().
@ -244,12 +239,10 @@ func restorePermissions(
pbody.SetRecipients([]models.DriveRecipientable{rec}) pbody.SetRecipients([]models.DriveRecipientable{rec})
np, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil) _, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil)
if err != nil { if err != nil {
return graph.Wrap(ctx, err, "setting permissions") return graph.Wrap(ctx, err, "setting permissions")
} }
permissionIDMappings[p.ID] = ptr.Val(np.GetValue()[0].GetId())
} }
return nil return nil

View File

@ -45,10 +45,6 @@ func RestoreCollections(
restoreMetrics support.CollectionMetrics restoreMetrics support.CollectionMetrics
metrics support.CollectionMetrics metrics support.CollectionMetrics
folderMetas map[string]Metadata folderMetas map[string]Metadata
// permissionIDMappings is used to map between old and new id
// of permissions as we restore them
permissionIDMappings = map[string]string{}
) )
ctx = clues.Add( ctx = clues.Add(
@ -82,7 +78,7 @@ func RestoreCollections(
"path", dc.FullPath()) // TODO: pii "path", dc.FullPath()) // TODO: pii
) )
metrics, folderMetas, permissionIDMappings, err = RestoreCollection( metrics, folderMetas, err = RestoreCollection(
ictx, ictx,
backupVersion, backupVersion,
service, service,
@ -91,7 +87,6 @@ func RestoreCollections(
OneDriveSource, OneDriveSource,
dest.ContainerName, dest.ContainerName,
deets, deets,
permissionIDMappings,
opts.RestorePermissions, opts.RestorePermissions,
errs) errs)
if err != nil { if err != nil {
@ -122,7 +117,8 @@ func RestoreCollections(
// RestoreCollection handles restoration of an individual collection. // RestoreCollection handles restoration of an individual collection.
// returns: // returns:
// - the collection's item and byte count metrics // - the collection's item and byte count metrics
// - the context cancellation state (true if the context is canceled) // - the updated metadata map that include metadata for folders in this collection
// - error, if any besides recoverable
func RestoreCollection( func RestoreCollection(
ctx context.Context, ctx context.Context,
backupVersion int, backupVersion int,
@ -132,10 +128,9 @@ func RestoreCollection(
source driveSource, source driveSource,
restoreContainerName string, restoreContainerName string,
deets *details.Builder, deets *details.Builder,
permissionIDMappings map[string]string,
restorePerms bool, restorePerms bool,
errs *fault.Bus, errs *fault.Bus,
) (support.CollectionMetrics, map[string]Metadata, map[string]string, error) { ) (support.CollectionMetrics, map[string]Metadata, error) {
var ( var (
metrics = support.CollectionMetrics{} metrics = support.CollectionMetrics{}
copyBuffer = make([]byte, copyBufferSize) copyBuffer = make([]byte, copyBufferSize)
@ -149,7 +144,7 @@ func RestoreCollection(
drivePath, err := path.ToOneDrivePath(directory) drivePath, err := path.ToOneDrivePath(directory)
if err != nil { if err != nil {
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "creating drive path").WithClues(ctx) return metrics, folderMetas, clues.Wrap(err, "creating drive path").WithClues(ctx)
} }
// Assemble folder hierarchy we're going to restore into (we recreate the folder hierarchy // Assemble folder hierarchy we're going to restore into (we recreate the folder hierarchy
@ -176,7 +171,7 @@ func RestoreCollection(
backupVersion, backupVersion,
restorePerms) restorePerms)
if err != nil { if err != nil {
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "getting permissions").WithClues(ctx) return metrics, folderMetas, clues.Wrap(err, "getting permissions").WithClues(ctx)
} }
// Create restore folders and get the folder ID of the folder the data stream will be restored in // Create restore folders and get the folder ID of the folder the data stream will be restored in
@ -185,10 +180,9 @@ func RestoreCollection(
service, service,
drivePath, drivePath,
restoreFolderElements, restoreFolderElements,
colMeta, colMeta)
permissionIDMappings)
if err != nil { if err != nil {
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "creating folders for restore") return metrics, folderMetas, clues.Wrap(err, "creating folders for restore")
} }
items := dc.Items(ctx, errs) items := dc.Items(ctx, errs)
@ -200,11 +194,11 @@ func RestoreCollection(
select { select {
case <-ctx.Done(): case <-ctx.Done():
return metrics, folderMetas, permissionIDMappings, err return metrics, folderMetas, err
case itemData, ok := <-items: case itemData, ok := <-items:
if !ok { if !ok {
return metrics, folderMetas, permissionIDMappings, nil return metrics, folderMetas, nil
} }
itemPath, err := dc.FullPath().Append(itemData.UUID(), true) itemPath, err := dc.FullPath().Append(itemData.UUID(), true)
@ -223,7 +217,6 @@ func RestoreCollection(
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
folderMetas, folderMetas,
permissionIDMappings,
restorePerms, restorePerms,
itemData, itemData,
itemPath) itemPath)
@ -260,7 +253,7 @@ func RestoreCollection(
} }
} }
return metrics, folderMetas, permissionIDMappings, el.Failure() return metrics, folderMetas, el.Failure()
} }
// restores an item, according to correct backup version behavior. // restores an item, according to correct backup version behavior.
@ -275,7 +268,6 @@ func restoreItem(
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
folderMetas map[string]Metadata, folderMetas map[string]Metadata,
permissionIDMappings map[string]string,
restorePerms bool, restorePerms bool,
itemData data.Stream, itemData data.Stream,
itemPath path.Path, itemPath path.Path,
@ -340,7 +332,6 @@ func restoreItem(
dc, dc,
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
permissionIDMappings,
restorePerms, restorePerms,
itemData) itemData)
if err != nil { if err != nil {
@ -360,7 +351,6 @@ func restoreItem(
dc, dc,
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
permissionIDMappings,
restorePerms, restorePerms,
itemData) itemData)
if err != nil { if err != nil {
@ -407,7 +397,6 @@ func restoreV1File(
fetcher fileFetcher, fetcher fileFetcher,
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
permissionIDMappings map[string]string,
restorePerms bool, restorePerms bool,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
@ -440,13 +429,12 @@ func restoreV1File(
return details.ItemInfo{}, clues.Wrap(err, "restoring file") return details.ItemInfo{}, clues.Wrap(err, "restoring file")
} }
err = restorePermissions( err = RestorePermissions(
ctx, ctx,
service, service,
drivePath.DriveID, drivePath.DriveID,
itemID, itemID,
meta, meta)
permissionIDMappings)
if err != nil { if err != nil {
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions") return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
} }
@ -462,7 +450,6 @@ func restoreV6File(
fetcher fileFetcher, fetcher fileFetcher,
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
permissionIDMappings map[string]string,
restorePerms bool, restorePerms bool,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
@ -506,13 +493,12 @@ func restoreV6File(
return itemInfo, nil return itemInfo, nil
} }
err = restorePermissions( err = RestorePermissions(
ctx, ctx,
service, service,
drivePath.DriveID, drivePath.DriveID,
itemID, itemID,
meta, meta,
permissionIDMappings,
) )
if err != nil { if err != nil {
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions") return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")

View File

@ -67,7 +67,7 @@ func RestoreCollections(
switch dc.FullPath().Category() { switch dc.FullPath().Category() {
case path.LibrariesCategory: case path.LibrariesCategory:
metrics, _, _, err = onedrive.RestoreCollection( metrics, _, err = onedrive.RestoreCollection(
ictx, ictx,
backupVersion, backupVersion,
service, service,
@ -76,7 +76,6 @@ func RestoreCollections(
onedrive.SharePointSource, onedrive.SharePointSource,
dest.ContainerName, dest.ContainerName,
deets, deets,
map[string]string{},
false, false,
errs) errs)
case path.ListsCategory: case path.ListsCategory:

View File

@ -455,6 +455,29 @@ func toDataLayerPath(
return p return p
} }
func mustGetDefaultDriveID(
t *testing.T,
ctx context.Context, //revive:disable-line:context-as-argument
service graph.Servicer,
userID string,
) string {
d, err := service.Client().UsersById(userID).Drive().Get(ctx, nil)
if err != nil {
err = graph.Wrap(
ctx,
err,
"retrieving default user drive").
With("user", userID)
}
require.Nil(t, clues.ToCore(err))
id := ptr.Val(d.GetId())
require.NotEmpty(t, id, "drive ID not set")
return id
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// integration tests // integration tests
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -1132,31 +1155,6 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
fault.New(true)) fault.New(true))
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// TODO: whomever can figure out a way to declare this outside of this func
// and not have the linter complain about unused is welcome to do so.
mustGetDefaultDriveID := func(
t *testing.T,
ctx context.Context, //revive:disable-line:context-as-argument
service graph.Servicer,
userID string,
) string {
d, err := service.Client().UsersById(userID).Drive().Get(ctx, nil)
if err != nil {
err = graph.Wrap(
ctx,
err,
"retrieving default user drive").
With("user", userID)
}
require.NoError(t, err)
id := ptr.Val(d.GetId())
require.NotEmpty(t, id, "drive ID not set")
return id
}
driveID := mustGetDefaultDriveID(t, ctx, gc.Service, suite.user) driveID := mustGetDefaultDriveID(t, ctx, gc.Service, suite.user)
fileDBF := func(id, timeStamp, subject, body string) []byte { fileDBF := func(id, timeStamp, subject, body string) []byte {
@ -1254,6 +1252,102 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
itemsRead: 1, // .data file for newitem itemsRead: 1, // .data file for newitem
itemsWritten: 3, // .data and .meta for newitem, .dirmeta for parent itemsWritten: 3, // .data and .meta for newitem, .dirmeta for parent
}, },
{
name: "add permission to new file",
updateUserData: func(t *testing.T) {
driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName)
driveItem.SetFile(models.NewFile())
err = onedrive.RestorePermissions(
ctx,
gc.Service,
driveID,
*newFile.GetId(),
onedrive.Metadata{
SharingMode: onedrive.SharingModeCustom,
Permissions: []onedrive.UserPermission{
{
Roles: []string{"write"},
EntityID: suite.user,
},
},
},
)
require.NoErrorf(t, err, "add permission to file %v", clues.ToCore(err))
},
itemsRead: 1, // .data file for newitem
itemsWritten: 2, // .meta for newitem, .dirmeta for parent (.data is not written as it is not updated)
},
{
name: "remove permission from new file",
updateUserData: func(t *testing.T) {
driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName)
driveItem.SetFile(models.NewFile())
err = onedrive.RestorePermissions(
ctx,
gc.Service,
driveID,
*newFile.GetId(),
onedrive.Metadata{
SharingMode: onedrive.SharingModeCustom,
Permissions: []onedrive.UserPermission{},
},
)
require.NoError(t, err, "add permission to file", clues.ToCore(err))
},
itemsRead: 1, // .data file for newitem
itemsWritten: 2, // .meta for newitem, .dirmeta for parent (.data is not written as it is not updated)
},
{
name: "add permission to container",
updateUserData: func(t *testing.T) {
targetContainer := containerIDs[container1]
driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName)
driveItem.SetFile(models.NewFile())
err = onedrive.RestorePermissions(
ctx,
gc.Service,
driveID,
targetContainer,
onedrive.Metadata{
SharingMode: onedrive.SharingModeCustom,
Permissions: []onedrive.UserPermission{
{
Roles: []string{"write"},
EntityID: suite.user,
},
},
},
)
require.NoError(t, err, "add permission to file", clues.ToCore(err))
},
itemsRead: 0,
itemsWritten: 1, // .dirmeta for collection
},
{
name: "remove permission from container",
updateUserData: func(t *testing.T) {
targetContainer := containerIDs[container1]
driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName)
driveItem.SetFile(models.NewFile())
err = onedrive.RestorePermissions(
ctx,
gc.Service,
driveID,
targetContainer,
onedrive.Metadata{
SharingMode: onedrive.SharingModeCustom,
Permissions: []onedrive.UserPermission{},
},
)
require.NoError(t, err, "add permission to file", clues.ToCore(err))
},
itemsRead: 0,
itemsWritten: 1, // .dirmeta for collection
},
{ {
name: "update contents of a file", name: "update contents of a file",
updateUserData: func(t *testing.T) { updateUserData: func(t *testing.T) {