Compare commits

...

5 Commits
main ... 3134-5

Author SHA1 Message Date
ryanfkeepers
79a7288d99 setting aside 2023-05-03 15:19:52 -06:00
ryanfkeepers
45c40993d4 fixup bad rebase state 2023-05-03 14:00:14 -06:00
ryanfkeepers
5754bbaef0 permission basics
this doesn't solve all permission handling in sharepoint, but
it sets a lot of groundwork that needs to be done anyway.
2023-05-03 13:56:01 -06:00
Keepers
b589490f11 rename toOneDrivePath to toDrivePath (#3238)
Easier to grok that the func should work for all drive-ish services this
way.
2023-05-03 12:56:37 -06:00
ryanfkeepers
95e1af794d move onedrive.MetadataItem to metadata.Item
In keeping with other changes that migrate shared metadata to
the onedrive/metadata pkg for exported access.
2023-05-03 12:56:37 -06:00
23 changed files with 302 additions and 223 deletions

View File

@ -49,10 +49,10 @@ const (
oneDriveServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef oneDriveServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef
corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef
# Restore file with ID 98765abcdef along with its associated permissions # Restore the file with ID 98765abcdef along with its associated permissions
corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef --restore-permissions corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef --restore-permissions
# Restore Alice's file named "FY2021 Planning.xlsx in "Documents/Finance Reports" from a specific backup # Restore Alice's file named "FY2021 Planning.xlsx" in "Documents/Finance Reports" from a specific backup
corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd \ corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd \
--user alice@example.com --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports" --user alice@example.com --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports"

View File

@ -33,6 +33,8 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
utils.AddBackupIDFlag(c, true) utils.AddBackupIDFlag(c, true)
utils.AddSharePointDetailsAndRestoreFlags(c) utils.AddSharePointDetailsAndRestoreFlags(c)
options.AddRestorePermissionsFlag(c)
options.AddFailFastFlag(c) options.AddFailFastFlag(c)
} }
@ -47,7 +49,11 @@ const (
sharePointServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef sharePointServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef
corso restore sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef corso restore sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef
# Restore a file named "ServerRenderTemplate.xsl in "Display Templates/Style Sheets". # Restore the file with ID 98765abcdef along with its associated permissions
corso restore sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd \
--file 98765abcdef --restore-permissions
# Restore a file named "ServerRenderTemplate.xsl" in "Display Templates/Style Sheets".
corso restore sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd \ corso restore sharepoint --backup 1234abcd-12ab-cd34-56de-1234abcd \
--file "ServerRenderTemplate.xsl" --folder "Display Templates/Style Sheets" --file "ServerRenderTemplate.xsl" --folder "Display Templates/Style Sheets"

View File

@ -43,4 +43,6 @@ var (
PageFolderInput = []string{"pageFolder1", "pageFolder2"} PageFolderInput = []string{"pageFolder1", "pageFolder2"}
PageInput = []string{"page1", "page2"} PageInput = []string{"page1", "page2"}
RestorePermissions = true
) )

View File

@ -218,7 +218,7 @@ func (gc *GraphConnector) ConsumeRestoreCollections(
case selectors.ServiceOneDrive: case selectors.ServiceOneDrive:
status, err = onedrive.RestoreCollections(ctx, creds, backupVersion, gc.Service, dest, opts, dcs, deets, errs) status, err = onedrive.RestoreCollections(ctx, creds, backupVersion, gc.Service, dest, opts, dcs, deets, errs)
case selectors.ServiceSharePoint: case selectors.ServiceSharePoint:
status, err = sharepoint.RestoreCollections(ctx, backupVersion, creds, gc.Service, dest, dcs, deets, errs) status, err = sharepoint.RestoreCollections(ctx, backupVersion, creds, gc.Service, dest, opts, dcs, deets, errs)
default: default:
err = clues.Wrap(clues.New(selector.Service.String()), "service not supported") err = clues.Wrap(clues.New(selector.Service.String()), "service not supported")
} }

View File

@ -228,10 +228,6 @@ func (m *SiteItemRequestBuilder) Patch(
return res.(msmodel.Siteable), nil return res.(msmodel.Siteable), nil
} }
// Permissions provides operations to manage the permissions property of the microsoft.graph.site entity.
// PermissionsById provides operations to manage the permissions property of the microsoft.graph.site entity.
// Sites provides operations to manage the sites property of the microsoft.graph.site entity.
// func (m *SiteItemRequestBuilder) Sites()
// SitesById provides operations to manage the sites property of the microsoft.graph.site entity. // SitesById provides operations to manage the sites property of the microsoft.graph.site entity.
// //
//nolint:revive,wsl //nolint:revive,wsl

View File

@ -722,14 +722,14 @@ func permissionEqual(expected metadata.Permission, got metadata.Permission) bool
return true return true
} }
func compareOneDriveItem( func compareDriveItem(
t *testing.T, t *testing.T,
expected map[string][]byte, expected map[string][]byte,
item data.Stream, item data.Stream,
restorePermissions bool, restorePermissions bool,
rootDir bool, rootDir bool,
) bool { ) bool {
// Skip OneDrive permissions in the folder that used to be the root. We don't // Skip Drive permissions in the folder that used to be the root. We don't
// have a good way to materialize these in the test right now. // have a good way to materialize these in the test right now.
if rootDir && item.UUID() == metadata.DirMetaFileSuffix { if rootDir && item.UUID() == metadata.DirMetaFileSuffix {
return false return false
@ -747,7 +747,7 @@ func compareOneDriveItem(
) )
if isMeta { if isMeta {
var itemType *onedrive.MetadataItem var itemType *metadata.Item
assert.IsType(t, itemType, item) assert.IsType(t, itemType, item)
} else { } else {
@ -824,8 +824,7 @@ func compareOneDriveItem(
t, t,
expectedMeta.Permissions, expectedMeta.Permissions,
itemPerms, itemPerms,
permissionEqual, permissionEqual)
)
return true return true
} }
@ -887,7 +886,7 @@ func compareItem(
} }
case path.OneDriveService: case path.OneDriveService:
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir) return compareDriveItem(t, expected, item, restorePermissions, rootDir)
case path.SharePointService: case path.SharePointService:
if category != path.LibrariesCategory { if category != path.LibrariesCategory {
@ -895,7 +894,7 @@ func compareItem(
} }
// SharePoint libraries reuses OneDrive code. // SharePoint libraries reuses OneDrive code.
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir) return compareDriveItem(t, expected, item, restorePermissions, rootDir)
default: default:
assert.FailNowf(t, "unexpected service: %s", service.String()) assert.FailNowf(t, "unexpected service: %s", service.String())

View File

@ -431,12 +431,6 @@ func (si suiteInfoImpl) Resource() Resource {
// SharePoint shares most of its libraries implementation with OneDrive so we // SharePoint shares most of its libraries implementation with OneDrive so we
// only test simple things here and leave the more extensive testing to // only test simple things here and leave the more extensive testing to
// OneDrive. // OneDrive.
//
// TODO(ashmrtn): SharePoint doesn't have permissions backup/restore enabled
// right now. Adjust the tests here when that is enabled so we have at least
// basic assurances that it's doing the right thing. We can leave the more
// extensive permissions tests to OneDrive as well.
type GraphConnectorSharePointIntegrationSuite struct { type GraphConnectorSharePointIntegrationSuite struct {
tester.Suite tester.Suite
suiteInfo suiteInfo
@ -486,6 +480,18 @@ func (suite *GraphConnectorSharePointIntegrationSuite) TestRestoreAndBackup_Mult
testRestoreAndBackupMultipleFilesAndFoldersNoPermissions(suite, version.Backup) testRestoreAndBackupMultipleFilesAndFoldersNoPermissions(suite, version.Backup)
} }
func (suite *GraphConnectorSharePointIntegrationSuite) TestPermissionsRestoreAndBackup() {
testPermissionsRestoreAndBackup(suite, version.Backup)
}
func (suite *GraphConnectorSharePointIntegrationSuite) TestPermissionsBackupAndNoRestore() {
testPermissionsBackupAndNoRestore(suite, version.Backup)
}
func (suite *GraphConnectorSharePointIntegrationSuite) TestPermissionsInheritanceRestoreAndBackup() {
testPermissionsInheritanceRestoreAndBackup(suite, version.Backup)
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// OneDrive most recent backup version // OneDrive most recent backup version
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -372,3 +372,21 @@ func GetFolderByName(
return foundItem, nil return foundItem, nil
} }
func PostItemPermissionUpdate(
ctx context.Context,
service graph.Servicer,
driveID, itemID string,
body *drive.ItemsItemInvitePostRequestBody,
) (drives.ItemItemsItemInviteResponseable, error) {
itm, err := service.Client().
DrivesById(driveID).
ItemsById(itemID).
Invite().
Post(ctx, body, nil)
if err != nil {
return nil, graph.Wrap(ctx, err, "posting permissions")
}
return itm, nil
}

View File

@ -37,8 +37,8 @@ var (
_ data.Stream = &Item{} _ data.Stream = &Item{}
_ data.StreamInfo = &Item{} _ data.StreamInfo = &Item{}
_ data.StreamModTime = &Item{} _ data.StreamModTime = &Item{}
_ data.Stream = &MetadataItem{} _ data.Stream = &metadata.Item{}
_ data.StreamModTime = &MetadataItem{} _ data.StreamModTime = &metadata.Item{}
) )
// Collection represents a set of OneDrive objects retrieved from M365 // Collection represents a set of OneDrive objects retrieved from M365
@ -120,12 +120,12 @@ func pathToLocation(p path.Path) (*path.Builder, error) {
return nil, nil return nil, nil
} }
odp, err := path.ToOneDrivePath(p) dp, err := path.ToDrivePath(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return path.Builder{}.Append(odp.Root).Append(odp.Folders...), nil return path.Builder{}.Append(dp.Root).Append(dp.Folders...), nil
} }
// NewCollection creates a Collection // NewCollection creates a Collection
@ -306,53 +306,14 @@ type Item struct {
info details.ItemInfo info details.ItemInfo
} }
func (od *Item) UUID() string {
return od.id
}
func (od *Item) ToReader() io.ReadCloser {
return od.data
}
// Deleted implements an interface function. However, OneDrive items are marked // Deleted implements an interface function. However, OneDrive items are marked
// as deleted by adding them to the exclude list so this can always return // as deleted by adding them to the exclude list so this can always return
// false. // false.
func (od Item) Deleted() bool { func (i Item) Deleted() bool { return false }
return false func (i *Item) UUID() string { return i.id }
} func (i *Item) ToReader() io.ReadCloser { return i.data }
func (i *Item) Info() details.ItemInfo { return i.info }
func (od *Item) Info() details.ItemInfo { func (i *Item) ModTime() time.Time { return i.info.Modified() }
return od.info
}
func (od *Item) ModTime() time.Time {
return od.info.Modified()
}
type MetadataItem struct {
id string
data io.ReadCloser
modTime time.Time
}
func (od *MetadataItem) UUID() string {
return od.id
}
func (od *MetadataItem) ToReader() io.ReadCloser {
return od.data
}
// Deleted implements an interface function. However, OneDrive items are marked
// as deleted by adding them to the exclude list so this can always return
// false.
func (od MetadataItem) Deleted() bool {
return false
}
func (od *MetadataItem) ModTime() time.Time {
return od.modTime
}
// getDriveItemContent fetch drive item's contents with retries // getDriveItemContent fetch drive item's contents with retries
func (oc *Collection) getDriveItemContent( func (oc *Collection) getDriveItemContent(
@ -602,12 +563,12 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) {
return progReader, nil return progReader, nil
}) })
oc.data <- &MetadataItem{ oc.data <- &metadata.Item{
id: metaFileName + metaSuffix, ID: metaFileName + metaSuffix,
data: metaReader, Data: metaReader,
// Metadata file should always use the latest time as // Metadata file should always use the latest time as
// permissions change does not update mod time. // permissions change does not update mod time.
modTime: time.Now(), Mod: time.Now(),
} }
// Item read successfully, add to collection // Item read successfully, add to collection

View File

@ -18,7 +18,9 @@ import (
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/connector/onedrive/api" "github.com/alcionai/corso/src/internal/connector/onedrive/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/api/mock" "github.com/alcionai/corso/src/internal/connector/onedrive/api/mock"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
@ -281,6 +283,7 @@ func (suite *OneDriveUnitSuite) TestDrives() {
type OneDriveSuite struct { type OneDriveSuite struct {
tester.Suite tester.Suite
userID string userID string
creds account.M365Config
} }
func TestOneDriveSuite(t *testing.T) { func TestOneDriveSuite(t *testing.T) {
@ -292,7 +295,15 @@ func TestOneDriveSuite(t *testing.T) {
} }
func (suite *OneDriveSuite) SetupSuite() { func (suite *OneDriveSuite) SetupSuite() {
suite.userID = tester.SecondaryM365UserID(suite.T()) t := suite.T()
suite.userID = tester.SecondaryM365UserID(t)
acct := tester.NewM365Account(t)
creds, err := acct.M365Config()
require.NoError(t, err)
suite.creds = creds
} }
func (suite *OneDriveSuite) TestCreateGetDeleteFolder() { func (suite *OneDriveSuite) TestCreateGetDeleteFolder() {
@ -333,17 +344,46 @@ func (suite *OneDriveSuite) TestCreateGetDeleteFolder() {
rootFolder, err := api.GetDriveRoot(ctx, gs, driveID) rootFolder, err := api.GetDriveRoot(ctx, gs, driveID)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
restoreFolders := path.Builder{}.Append(folderElements...) restoreDir := path.Builder{}.Append(folderElements...)
drivePath := path.DrivePath{
DriveID: driveID,
Root: "root:",
Folders: folderElements,
}
folderID, err := CreateRestoreFolders(ctx, gs, driveID, ptr.Val(rootFolder.GetId()), restoreFolders, NewFolderCache()) folderID, err := CreateRestoreFolders(
ctx,
suite.creds,
gs,
&drivePath,
ptr.Val(rootFolder.GetId()),
restoreDir,
nil, // only needed for permissions
metadata.Metadata{},
map[string]metadata.Metadata{},
NewFolderCache(),
map[string]string{},
false)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
folderIDs = append(folderIDs, folderID) folderIDs = append(folderIDs, folderID)
folderName2 := "Corso_Folder_Test_" + dttm.FormatNow(dttm.SafeForTesting) folderName2 := "Corso_Folder_Test_" + dttm.FormatNow(dttm.SafeForTesting)
restoreFolders = restoreFolders.Append(folderName2) restoreDir = restoreDir.Append(folderName2)
folderID, err = CreateRestoreFolders(ctx, gs, driveID, ptr.Val(rootFolder.GetId()), restoreFolders, NewFolderCache()) folderID, err = CreateRestoreFolders(
ctx,
suite.creds,
gs,
&drivePath,
ptr.Val(rootFolder.GetId()),
restoreDir,
nil, // only needed for permissions
metadata.Metadata{},
map[string]metadata.Metadata{},
NewFolderCache(),
map[string]string{},
false)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
folderIDs = append(folderIDs, folderID) folderIDs = append(folderIDs, folderID)

View File

@ -63,7 +63,6 @@ func sharePointItemMetaReader(
driveID string, driveID string,
item models.DriveItemable, item models.DriveItemable,
) (io.ReadCloser, int, error) { ) (io.ReadCloser, int, error) {
// TODO: include permissions
return baseItemMetaReader(ctx, service, driveID, item) return baseItemMetaReader(ctx, service, driveID, item)
} }
@ -243,23 +242,23 @@ func filterUserPermissions(ctx context.Context, perms []models.Permissionable) [
// read - Read // read - Read
// empty - Restricted View // empty - Restricted View
roles := p.GetRoles() roles := p.GetRoles()
entityID := "" entityID := ""
if gv2.GetUser() != nil {
switch true {
case gv2.GetUser() != nil:
entityID = ptr.Val(gv2.GetUser().GetId()) entityID = ptr.Val(gv2.GetUser().GetId())
} else if gv2.GetGroup() != nil { case gv2.GetGroup() != nil:
entityID = ptr.Val(gv2.GetGroup().GetId()) entityID = ptr.Val(gv2.GetGroup().GetId())
} else { case gv2.GetApplication() != nil:
// TODO Add application permissions when adding permissions for SharePoint entityID = ptr.Val(gv2.GetApplication().GetId())
// https://devblogs.microsoft.com/microsoft365dev/controlling-app-access-on-specific-sharepoint-site-collections/ case gv2.GetDevice() != nil:
logm := logger.Ctx(ctx) entityID = ptr.Val(gv2.GetDevice().GetId())
if gv2.GetApplication() != nil { default:
logm.With("application_id", ptr.Val(gv2.GetApplication().GetId())) logger.Ctx(ctx).Info("untracked permission")
} }
if gv2.GetDevice() != nil {
logm.With("device_id", ptr.Val(gv2.GetDevice().GetId())) if gv2.GetDevice() != nil {
} logger.Ctx(ctx).With("device_id", ptr.Val(gv2.GetDevice().GetId()))
logm.Info("untracked permission")
} }
// Technically GrantedToV2 can also contain devices, but the // Technically GrantedToV2 can also contain devices, but the

View File

@ -288,7 +288,7 @@ func TestItemUnitTestSuite(t *testing.T) {
suite.Run(t, &ItemUnitTestSuite{Suite: tester.NewUnitSuite(t)}) suite.Run(t, &ItemUnitTestSuite{Suite: tester.NewUnitSuite(t)})
} }
func (suite *ItemUnitTestSuite) TestOneDrivePermissionsFilter() { func (suite *ItemUnitTestSuite) TestDrivePermissionsFilter() {
permID := "fakePermId" permID := "fakePermId"
userID := "fakeuser@provider.com" userID := "fakeuser@provider.com"
userID2 := "fakeuser2@provider.com" userID2 := "fakeuser2@provider.com"

View File

@ -1,5 +1,10 @@
package metadata package metadata
import (
"io"
"time"
)
// ItemMeta contains metadata about the Item. It gets stored in a // ItemMeta contains metadata about the Item. It gets stored in a
// separate file in kopia // separate file in kopia
type Metadata struct { type Metadata struct {
@ -10,3 +15,17 @@ type Metadata struct {
SharingMode SharingMode `json:"permissionMode,omitempty"` SharingMode SharingMode `json:"permissionMode,omitempty"`
Permissions []Permission `json:"permissions,omitempty"` Permissions []Permission `json:"permissions,omitempty"`
} }
type Item struct {
ID string
Data io.ReadCloser
Mod time.Time
}
// Deleted implements an interface function. However, OneDrive items are marked
// as deleted by adding them to the exclude list so this can always return
// false.
func (i *Item) Deleted() bool { return false }
func (i *Item) UUID() string { return i.ID }
func (i *Item) ToReader() io.ReadCloser { return i.Data }
func (i *Item) ModTime() time.Time { return i.Mod }

View File

@ -2,13 +2,15 @@ package onedrive
import ( import (
"context" "context"
"fmt"
"github.com/alcionai/clues" "github.com/alcionai/clues"
msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive" "github.com/microsoftgraph/msgraph-sdk-go/drive"
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr" "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/connector/onedrive/api"
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata" "github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
"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"
@ -22,12 +24,12 @@ func getParentMetadata(
) (metadata.Metadata, error) { ) (metadata.Metadata, error) {
parentMeta, ok := metas[parentPath.String()] parentMeta, ok := metas[parentPath.String()]
if !ok { if !ok {
onedrivePath, err := path.ToOneDrivePath(parentPath) drivePath, err := path.ToDrivePath(parentPath)
if err != nil { if err != nil {
return metadata.Metadata{}, clues.Wrap(err, "invalid restore path") return metadata.Metadata{}, clues.Wrap(err, "invalid restore path")
} }
if len(onedrivePath.Folders) != 0 { if len(drivePath.Folders) != 0 {
return metadata.Metadata{}, clues.Wrap(err, "computing item permissions") return metadata.Metadata{}, clues.Wrap(err, "computing item permissions")
} }
@ -41,7 +43,7 @@ func getCollectionMetadata(
ctx context.Context, ctx context.Context,
drivePath *path.DrivePath, drivePath *path.DrivePath,
dc data.RestoreCollection, dc data.RestoreCollection,
metas map[string]metadata.Metadata, parentDirToMeta map[string]metadata.Metadata,
backupVersion int, backupVersion int,
restorePerms bool, restorePerms bool,
) (metadata.Metadata, error) { ) (metadata.Metadata, error) {
@ -59,12 +61,16 @@ func getCollectionMetadata(
return metadata.Metadata{}, nil return metadata.Metadata{}, nil
} }
fmt.Printf("\n-----\nbackupVersion %+v\n-----\n", backupVersion)
if backupVersion < version.OneDrive4DirIncludesPermissions { if backupVersion < version.OneDrive4DirIncludesPermissions {
colMeta, err := getParentMetadata(collectionPath, metas) colMeta, err := getParentMetadata(collectionPath, parentDirToMeta)
if err != nil { if err != nil {
return metadata.Metadata{}, clues.Wrap(err, "collection metadata") return metadata.Metadata{}, clues.Wrap(err, "collection metadata")
} }
fmt.Printf("\n-----\ngot colMeta %+v\n-----\n", colMeta)
return colMeta, nil return colMeta, nil
} }
@ -88,7 +94,11 @@ func getCollectionMetadata(
// traversing folderMetas and finding the first item with custom // traversing folderMetas and finding the first item with custom
// permissions. folderMetas is expected to have all the parent // permissions. folderMetas is expected to have all the parent
// directory metas for this to work. // directory metas for this to work.
func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadata.Metadata) (metadata.Metadata, error) { func computeParentPermissions(
originDir path.Path,
// map parent dir -> parent's metadata
parentDirToMeta map[string]metadata.Metadata,
) (metadata.Metadata, error) {
var ( var (
parent path.Path parent path.Path
meta metadata.Metadata meta metadata.Metadata
@ -97,7 +107,7 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat
ok bool ok bool
) )
parent = itemPath parent = originDir
for { for {
parent, err = parent.Dir() parent, err = parent.Dir()
@ -105,21 +115,24 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat
return metadata.Metadata{}, clues.New("getting parent") return metadata.Metadata{}, clues.New("getting parent")
} }
onedrivePath, err := path.ToOneDrivePath(parent) drivePath, err := path.ToDrivePath(parent)
if err != nil { if err != nil {
return metadata.Metadata{}, clues.New("get parent path") return metadata.Metadata{}, clues.New("transforming dir to drivePath")
} }
if len(onedrivePath.Folders) == 0 { if len(drivePath.Folders) == 0 {
return metadata.Metadata{}, nil return metadata.Metadata{}, nil
} }
meta, ok = folderMetas[parent.String()] meta, ok = parentDirToMeta[parent.String()]
if !ok { if !ok {
return metadata.Metadata{}, clues.New("no parent meta") return metadata.Metadata{}, clues.
New("parent permissions not found").
With("parent_dir", parent)
} }
if meta.SharingMode == metadata.SharingModeCustom { if meta.SharingMode == metadata.SharingModeCustom {
fmt.Printf("\n-----\nbefore return %+v\n-----\n", parentDirToMeta)
return meta, nil return meta, nil
} }
} }
@ -134,7 +147,7 @@ func UpdatePermissions(
driveID string, driveID string,
itemID string, itemID string,
permAdded, permRemoved []metadata.Permission, permAdded, permRemoved []metadata.Permission,
permissionIDMappings map[string]string, oldPermIDToNewID map[string]string,
) error { ) error {
// The ordering of the operations is important here. We first // The ordering of the operations is important here. We first
// remove all the removed permissions and then add the added ones. // remove all the removed permissions and then add the added ones.
@ -148,7 +161,7 @@ func UpdatePermissions(
return graph.Wrap(ctx, err, "creating delete client") return graph.Wrap(ctx, err, "creating delete client")
} }
pid, ok := permissionIDMappings[p.ID] pid, ok := oldPermIDToNewID[p.ID]
if !ok { if !ok {
return clues.New("no new permission id").WithClues(ctx) return clues.New("no new permission id").WithClues(ctx)
} }
@ -179,7 +192,7 @@ func UpdatePermissions(
continue continue
} }
pbody := msdrive.NewItemsItemInvitePostRequestBody() pbody := drive.NewItemsItemInvitePostRequestBody()
pbody.SetRoles(roles) pbody.SetRoles(roles)
if p.Expiration != nil { if p.Expiration != nil {
@ -204,12 +217,12 @@ func UpdatePermissions(
pbody.SetRecipients([]models.DriveRecipientable{rec}) pbody.SetRecipients([]models.DriveRecipientable{rec})
np, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil) newPerm, err := api.PostItemPermissionUpdate(ctx, service, driveID, itemID, pbody)
if err != nil { if err != nil {
return graph.Wrap(ctx, err, "setting permissions") return err
} }
permissionIDMappings[p.ID] = ptr.Val(np.GetValue()[0].GetId()) oldPermIDToNewID[p.ID] = ptr.Val(newPerm.GetValue()[0].GetId())
} }
return nil return nil
@ -225,23 +238,28 @@ func RestorePermissions(
service graph.Servicer, service graph.Servicer,
driveID string, driveID string,
itemID string, itemID string,
itemPath path.Path, originDir path.Path,
meta metadata.Metadata, current metadata.Metadata,
folderMetas map[string]metadata.Metadata, // map parent dir -> parent's metadata
permissionIDMappings map[string]string, parentDirToMeta map[string]metadata.Metadata,
oldPermIDToNewID map[string]string,
) error { ) error {
if meta.SharingMode == metadata.SharingModeInherited { if current.SharingMode == metadata.SharingModeInherited {
return nil return nil
} }
ctx = clues.Add(ctx, "permission_item_id", itemID) ctx = clues.Add(ctx, "permission_item_id", itemID)
parentPermissions, err := computeParentPermissions(itemPath, folderMetas) fmt.Printf("\n-----\nbefore %+v\n-----\n", parentDirToMeta)
parents, err := computeParentPermissions(originDir, parentDirToMeta)
if err != nil { if err != nil {
return clues.Wrap(err, "parent permissions").WithClues(ctx) return clues.Wrap(err, "parent permissions").WithClues(ctx)
} }
permAdded, permRemoved := metadata.DiffPermissions(parentPermissions.Permissions, meta.Permissions) fmt.Printf("\n-----\nafter %+v\n-----\n", parentDirToMeta)
return UpdatePermissions(ctx, creds, service, driveID, itemID, permAdded, permRemoved, permissionIDMappings) permAdded, permRemoved := metadata.DiffPermissions(parents.Permissions, current.Permissions)
return UpdatePermissions(ctx, creds, service, driveID, itemID, permAdded, permRemoved, oldPermIDToNewID)
} }

View File

@ -22,28 +22,39 @@ func TestPermissionsUnitTestSuite(t *testing.T) {
suite.Run(t, &PermissionsUnitTestSuite{Suite: tester.NewUnitSuite(t)}) suite.Run(t, &PermissionsUnitTestSuite{Suite: tester.NewUnitSuite(t)})
} }
func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions() { func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions_oneDrive() {
runComputeParentPermissionsTest(suite, path.OneDriveService, path.FilesCategory, "user")
}
func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions_sharePoint() {
runComputeParentPermissionsTest(suite, path.SharePointService, path.LibrariesCategory, "site")
}
func runComputeParentPermissionsTest(
suite *PermissionsUnitTestSuite,
service path.ServiceType,
category path.CategoryType,
resourceOwner string,
) {
entryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/level0/level1/level2/entry" entryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/level0/level1/level2/entry"
rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry" rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry"
entry, err := path.Build( entry, err := path.Build(
"tenant", "tenant",
"user", resourceOwner,
path.OneDriveService, service,
path.FilesCategory, category,
false, false,
strings.Split(entryPath, "/")..., strings.Split(entryPath, "/")...)
)
require.NoError(suite.T(), err, "creating path") require.NoError(suite.T(), err, "creating path")
rootEntry, err := path.Build( rootEntry, err := path.Build(
"tenant", "tenant",
"user", resourceOwner,
path.OneDriveService, service,
path.FilesCategory, category,
false, false,
strings.Split(rootEntryPath, "/")..., strings.Split(rootEntryPath, "/")...)
)
require.NoError(suite.T(), err, "creating path") require.NoError(suite.T(), err, "creating path")
level2, err := entry.Dir() level2, err := entry.Dir()

View File

@ -3,6 +3,7 @@ package onedrive
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"runtime/trace" "runtime/trace"
"sort" "sort"
@ -46,15 +47,15 @@ func RestoreCollections(
errs *fault.Bus, errs *fault.Bus,
) (*support.ConnectorOperationStatus, error) { ) (*support.ConnectorOperationStatus, error) {
var ( var (
restoreMetrics support.CollectionMetrics restoreMetrics support.CollectionMetrics
metrics support.CollectionMetrics metrics support.CollectionMetrics
folderMetas = map[string]metadata.Metadata{} parentDirToMeta = map[string]metadata.Metadata{}
// permissionIDMappings is used to map between old and new id // oldPermIDToNewID is used to map between old and new id
// of permissions as we restore them // of permissions as we restore them
permissionIDMappings = map[string]string{} oldPermIDToNewID = map[string]string{}
fc = NewFolderCache() fc = NewFolderCache()
rootIDCache = map[string]string{} rootFolderIDCache = map[string]string{}
) )
ctx = clues.Add( ctx = clues.Add(
@ -91,10 +92,10 @@ func RestoreCollections(
backupVersion, backupVersion,
service, service,
dc, dc,
folderMetas, parentDirToMeta,
permissionIDMappings, oldPermIDToNewID,
fc, fc,
rootIDCache, rootFolderIDCache,
OneDriveSource, OneDriveSource,
dest.ContainerName, dest.ContainerName,
deets, deets,
@ -132,10 +133,12 @@ func RestoreCollection(
backupVersion int, backupVersion int,
service graph.Servicer, service graph.Servicer,
dc data.RestoreCollection, dc data.RestoreCollection,
folderMetas map[string]metadata.Metadata, // cache of parent dir -> parent's metadata,
permissionIDMappings map[string]string, // mutated during this call
parentDirToMeta map[string]metadata.Metadata,
oldPermIDToNewID map[string]string,
fc *folderCache, fc *folderCache,
rootIDCache map[string]string, // map of drive id -> root folder ID rootFolderIDCache map[string]string, // map of drive id -> root folder ID
source driveSource, source driveSource,
restoreContainerName string, restoreContainerName string,
deets *details.Builder, deets *details.Builder,
@ -152,69 +155,75 @@ func RestoreCollection(
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory)) ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
defer end() defer end()
drivePath, err := path.ToOneDrivePath(directory) drivePath, err := path.ToDrivePath(directory)
if err != nil { if err != nil {
return metrics, clues.Wrap(err, "creating drive path").WithClues(ctx) return metrics, clues.Wrap(err, "creating drive path").WithClues(ctx)
} }
if rootIDCache == nil { if rootFolderIDCache == nil {
rootIDCache = map[string]string{} rootFolderIDCache = map[string]string{}
} }
if _, ok := rootIDCache[drivePath.DriveID]; !ok { if _, ok := rootFolderIDCache[drivePath.DriveID]; !ok {
root, err := api.GetDriveRoot(ctx, service, drivePath.DriveID) root, err := api.GetDriveRoot(ctx, service, drivePath.DriveID)
if err != nil { if err != nil {
return metrics, clues.Wrap(err, "getting drive root id") return metrics, clues.Wrap(err, "getting drive root id")
} }
rootIDCache[drivePath.DriveID] = ptr.Val(root.GetId()) rootFolderIDCache[drivePath.DriveID] = ptr.Val(root.GetId())
} }
// 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
// from the backup under this the restore folder instead of root) // from the backup under this the restore folder instead of root)
// i.e. Restore into `<restoreContainerName>/<original folder path>` // i.e. Restore into `<restoreContainerName>/<original folder path>`
// the drive into which this folder gets restored is tracked separately in drivePath. // the drive into which this folder gets restored is tracked separately in drivePath.
restoreFolderElements := path.Builder{}.Append(restoreContainerName).Append(drivePath.Folders...) restoreDir := path.Builder{}.Append(restoreContainerName).Append(drivePath.Folders...)
ctx = clues.Add( ctx = clues.Add(
ctx, ctx,
"directory", dc.FullPath().Folder(false), "directory", dc.FullPath().Folder(false),
"destination_elements", restoreFolderElements, "destination_dir", restoreDir,
"drive_id", drivePath.DriveID) "drive_id", drivePath.DriveID)
trace.Log(ctx, "gc:oneDrive:restoreCollection", directory.String()) trace.Log(ctx, "gc:oneDrive:restoreCollection", directory.String())
logger.Ctx(ctx).Info("restoring onedrive collection") logger.Ctx(ctx).Info("restoring onedrive collection")
fmt.Printf("\n-----\n1 %+v\n-----\n", parentDirToMeta)
colMeta, err := getCollectionMetadata( colMeta, err := getCollectionMetadata(
ctx, ctx,
drivePath, drivePath,
dc, dc,
folderMetas, parentDirToMeta,
backupVersion, backupVersion,
restorePerms) restorePerms)
if err != nil { if err != nil {
return metrics, clues.Wrap(err, "getting permissions").WithClues(ctx) return metrics, clues.Wrap(err, "getting permissions").WithClues(ctx)
} }
fmt.Printf("\n-----\n2 %+v\n-----\n", parentDirToMeta)
// 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
restoreFolderID, err := createRestoreFoldersWithPermissions( restoreFolderID, err := CreateRestoreFolders(
ctx, ctx,
creds, creds,
service, service,
drivePath, drivePath,
rootIDCache[drivePath.DriveID], rootFolderIDCache[drivePath.DriveID],
restoreFolderElements, restoreDir,
dc.FullPath(), dc.FullPath(),
colMeta, colMeta,
folderMetas, parentDirToMeta,
fc, fc,
permissionIDMappings, oldPermIDToNewID,
restorePerms) restorePerms)
if err != nil { if err != nil {
return metrics, clues.Wrap(err, "creating folders for restore") return metrics, clues.Wrap(err, "creating folders for restore")
} }
folderMetas[dc.FullPath().String()] = colMeta fmt.Printf("\n-----\nsetting %s\nto %+v\n-----\n", dc.FullPath(), colMeta)
parentDirToMeta[dc.FullPath().String()] = colMeta
items := dc.Items(ctx, errs) items := dc.Items(ctx, errs)
for { for {
@ -247,8 +256,8 @@ func RestoreCollection(
drivePath, drivePath,
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
folderMetas, parentDirToMeta,
permissionIDMappings, oldPermIDToNewID,
restorePerms, restorePerms,
itemData, itemData,
itemPath) itemPath)
@ -298,8 +307,8 @@ func restoreItem(
drivePath *path.DrivePath, drivePath *path.DrivePath,
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
folderMetas map[string]metadata.Metadata, parentDirToMeta map[string]metadata.Metadata,
permissionIDMappings map[string]string, oldPermIDToNewID map[string]string,
restorePerms bool, restorePerms bool,
itemData data.Stream, itemData data.Stream,
itemPath path.Path, itemPath path.Path,
@ -348,7 +357,7 @@ func restoreItem(
} }
trimmedPath := strings.TrimSuffix(itemPath.String(), metadata.DirMetaFileSuffix) trimmedPath := strings.TrimSuffix(itemPath.String(), metadata.DirMetaFileSuffix)
folderMetas[trimmedPath] = meta parentDirToMeta[trimmedPath] = meta
return details.ItemInfo{}, true, nil return details.ItemInfo{}, true, nil
} }
@ -366,8 +375,8 @@ func restoreItem(
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
restorePerms, restorePerms,
folderMetas, parentDirToMeta,
permissionIDMappings, oldPermIDToNewID,
itemPath, itemPath,
itemData) itemData)
if err != nil { if err != nil {
@ -389,8 +398,8 @@ func restoreItem(
restoreFolderID, restoreFolderID,
copyBuffer, copyBuffer,
restorePerms, restorePerms,
folderMetas, parentDirToMeta,
permissionIDMappings, oldPermIDToNewID,
itemPath, itemPath,
itemData) itemData)
if err != nil { if err != nil {
@ -439,8 +448,8 @@ func restoreV1File(
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
restorePerms bool, restorePerms bool,
folderMetas map[string]metadata.Metadata, parentDirToMeta map[string]metadata.Metadata,
permissionIDMappings map[string]string, oldPermIDToNewID map[string]string,
itemPath path.Path, itemPath path.Path,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
@ -481,8 +490,8 @@ func restoreV1File(
itemID, itemID,
itemPath, itemPath,
meta, meta,
folderMetas, parentDirToMeta,
permissionIDMappings) oldPermIDToNewID)
if err != nil { if err != nil {
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions") return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
} }
@ -500,8 +509,8 @@ func restoreV6File(
restoreFolderID string, restoreFolderID string,
copyBuffer []byte, copyBuffer []byte,
restorePerms bool, restorePerms bool,
folderMetas map[string]metadata.Metadata, parentDirToMeta map[string]metadata.Metadata,
permissionIDMappings map[string]string, oldPermIDToNewID map[string]string,
itemPath path.Path, itemPath path.Path,
itemData data.Stream, itemData data.Stream,
) (details.ItemInfo, error) { ) (details.ItemInfo, error) {
@ -553,8 +562,8 @@ func restoreV6File(
itemID, itemID,
itemPath, itemPath,
meta, meta,
folderMetas, parentDirToMeta,
permissionIDMappings) oldPermIDToNewID)
if err != nil { if err != nil {
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions") return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
} }
@ -562,31 +571,31 @@ func restoreV6File(
return itemInfo, nil return itemInfo, nil
} }
// createRestoreFoldersWithPermissions creates the restore folder hierarchy in // CreateRestoreFolders creates the restore folder hierarchy in
// the specified drive and returns the folder ID of the last folder entry in the // the specified drive and returns the folder ID of the last folder entry in the
// hierarchy. Permissions are only applied to the last folder in the hierarchy. // hierarchy. Permissions are only applied to the last folder in the hierarchy.
// Passing nil for the permissions results in just creating the folder(s). // Passing nil for the permissions results in just creating the folder(s).
// folderCache is mutated, as a side effect of populating the items. // folderCache is mutated, as a side effect of populating the items.
func createRestoreFoldersWithPermissions( func CreateRestoreFolders(
ctx context.Context, ctx context.Context,
creds account.M365Config, creds account.M365Config,
service graph.Servicer, service graph.Servicer,
drivePath *path.DrivePath, drivePath *path.DrivePath,
driveRootID string, driveRootFolderID string,
restoreFolders *path.Builder, restoreDir *path.Builder,
folderPath path.Path, originDir path.Path,
folderMetadata metadata.Metadata, colMeta metadata.Metadata,
folderMetas map[string]metadata.Metadata, parentDirToMeta map[string]metadata.Metadata,
fc *folderCache, fc *folderCache,
permissionIDMappings map[string]string, oldPermIDToNewID map[string]string,
restorePerms bool, restorePerms bool,
) (string, error) { ) (string, error) {
id, err := CreateRestoreFolders( id, err := createRestoreFolders(
ctx, ctx,
service, service,
drivePath.DriveID, drivePath.DriveID,
driveRootID, driveRootFolderID,
restoreFolders, restoreDir,
fc) fc)
if err != nil { if err != nil {
return "", err return "", err
@ -607,28 +616,28 @@ func createRestoreFoldersWithPermissions(
service, service,
drivePath.DriveID, drivePath.DriveID,
id, id,
folderPath, originDir,
folderMetadata, colMeta,
folderMetas, parentDirToMeta,
permissionIDMappings) oldPermIDToNewID)
return id, err return id, err
} }
// CreateRestoreFolders creates the restore folder hierarchy in the specified // createRestoreFolders creates the restore folder hierarchy in the specified
// drive and returns the folder ID of the last folder entry in the hierarchy. // drive and returns the folder ID of the last folder entry in the hierarchy.
// folderCache is mutated, as a side effect of populating the items. // folderCache is mutated, as a side effect of populating the items.
func CreateRestoreFolders( func createRestoreFolders(
ctx context.Context, ctx context.Context,
service graph.Servicer, service graph.Servicer,
driveID, driveRootID string, driveID, driveRootID string,
restoreFolders *path.Builder, restoreDir *path.Builder,
fc *folderCache, fc *folderCache,
) (string, error) { ) (string, error) {
var ( var (
location = &path.Builder{} location = &path.Builder{}
parentFolderID = driveRootID parentFolderID = driveRootID
folders = restoreFolders.Elements() folders = restoreDir.Elements()
) )
for _, folder := range folders { for _, folder := range folders {
@ -791,12 +800,12 @@ func AugmentRestorePaths(backupVersion int, paths []path.Path) ([]path.Path, err
return nil, err return nil, err
} }
onedrivePath, err := path.ToOneDrivePath(np) drivePath, err := path.ToDrivePath(np)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(onedrivePath.Folders) == 0 { if len(drivePath.Folders) == 0 {
break break
} }

View File

@ -23,6 +23,8 @@ func (ms *MockGraphService) Adapter() *msgraphsdk.GraphRequestAdapter {
return nil return nil
} }
var _ graph.Servicer = &oneDriveService{}
// TODO(ashmrtn): Merge with similar structs in graph and exchange packages. // TODO(ashmrtn): Merge with similar structs in graph and exchange packages.
type oneDriveService struct { type oneDriveService struct {
client msgraphsdk.GraphServiceClient client msgraphsdk.GraphServiceClient

View File

@ -86,9 +86,7 @@ func DataCollections(
} }
case path.LibrariesCategory: case path.LibrariesCategory:
var excludes map[string]map[string]struct{} spcs, excluded, err = collectLibraries(
spcs, excludes, err = collectLibraries(
ctx, ctx,
itemClient, itemClient,
serv, serv,
@ -104,7 +102,7 @@ func DataCollections(
continue continue
} }
for prefix, excludes := range excludes { for prefix, excludes := range excluded {
if _, ok := excluded[prefix]; !ok { if _, ok := excluded[prefix]; !ok {
excluded[prefix] = map[string]struct{}{} excluded[prefix] = map[string]struct{}{}
} }

View File

@ -46,6 +46,7 @@ func RestoreCollections(
creds account.M365Config, creds account.M365Config,
service graph.Servicer, service graph.Servicer,
dest control.RestoreDestination, dest control.RestoreDestination,
opts control.Options,
dcs []data.RestoreCollection, dcs []data.RestoreCollection,
deets *details.Builder, deets *details.Builder,
errs *fault.Bus, errs *fault.Bus,
@ -82,7 +83,7 @@ func RestoreCollections(
onedrive.SharePointSource, onedrive.SharePointSource,
dest.ContainerName, dest.ContainerName,
deets, deets,
false, opts.RestorePermissions,
errs) errs)
case path.ListsCategory: case path.ListsCategory:

View File

@ -386,13 +386,16 @@ func generateContainerOfItems(
dest, dest,
collections) collections)
opts := control.Defaults()
opts.RestorePermissions = true
deets, err := gc.ConsumeRestoreCollections( deets, err := gc.ConsumeRestoreCollections(
ctx, ctx,
backupVersion, backupVersion,
acct, acct,
sel, sel,
dest, dest,
control.Options{RestorePermissions: true}, opts,
dataColls, dataColls,
fault.New(true)) fault.New(true))
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
@ -1333,7 +1336,6 @@ func runDriveIncrementalTest(
updateFiles func(t *testing.T) updateFiles func(t *testing.T)
itemsRead int itemsRead int
itemsWritten int itemsWritten int
skip bool
}{ }{
{ {
name: "clean incremental, no changes", name: "clean incremental, no changes",
@ -1361,7 +1363,6 @@ func runDriveIncrementalTest(
}, },
{ {
name: "add permission to new file", name: "add permission to new file",
skip: skipPermissionsTests,
updateFiles: func(t *testing.T) { updateFiles: func(t *testing.T) {
driveItem := models.NewDriveItem() driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName) driveItem.SetName(&newFileName)
@ -1382,7 +1383,6 @@ func runDriveIncrementalTest(
}, },
{ {
name: "remove permission from new file", name: "remove permission from new file",
skip: skipPermissionsTests,
updateFiles: func(t *testing.T) { updateFiles: func(t *testing.T) {
driveItem := models.NewDriveItem() driveItem := models.NewDriveItem()
driveItem.SetName(&newFileName) driveItem.SetName(&newFileName)
@ -1396,14 +1396,13 @@ func runDriveIncrementalTest(
[]metadata.Permission{}, []metadata.Permission{},
[]metadata.Permission{writePerm}, []metadata.Permission{writePerm},
permissionIDMappings) permissionIDMappings)
require.NoErrorf(t, err, "adding permission to file %v", clues.ToCore(err)) require.NoErrorf(t, err, "removing permission from file %v", clues.ToCore(err))
}, },
itemsRead: 1, // .data file for newitem itemsRead: 1, // .data file for newitem
itemsWritten: 2, // .meta for newitem, .dirmeta for parent (.data is not written as it is not updated) itemsWritten: 2, // .meta for newitem, .dirmeta for parent (.data is not written as it is not updated)
}, },
{ {
name: "add permission to container", name: "add permission to container",
skip: skipPermissionsTests,
updateFiles: func(t *testing.T) { updateFiles: func(t *testing.T) {
targetContainer := containerIDs[container1] targetContainer := containerIDs[container1]
driveItem := models.NewDriveItem() driveItem := models.NewDriveItem()
@ -1418,14 +1417,13 @@ func runDriveIncrementalTest(
[]metadata.Permission{writePerm}, []metadata.Permission{writePerm},
[]metadata.Permission{}, []metadata.Permission{},
permissionIDMappings) permissionIDMappings)
require.NoErrorf(t, err, "adding permission to file %v", clues.ToCore(err)) require.NoErrorf(t, err, "adding permission to container %v", clues.ToCore(err))
}, },
itemsRead: 0, itemsRead: 0,
itemsWritten: 1, // .dirmeta for collection itemsWritten: 1, // .dirmeta for collection
}, },
{ {
name: "remove permission from container", name: "remove permission from container",
skip: skipPermissionsTests,
updateFiles: func(t *testing.T) { updateFiles: func(t *testing.T) {
targetContainer := containerIDs[container1] targetContainer := containerIDs[container1]
driveItem := models.NewDriveItem() driveItem := models.NewDriveItem()
@ -1440,7 +1438,7 @@ func runDriveIncrementalTest(
[]metadata.Permission{}, []metadata.Permission{},
[]metadata.Permission{writePerm}, []metadata.Permission{writePerm},
permissionIDMappings) permissionIDMappings)
require.NoErrorf(t, err, "adding permission to file %v", clues.ToCore(err)) require.NoErrorf(t, err, "removing permission from container %v", clues.ToCore(err))
}, },
itemsRead: 0, itemsRead: 0,
itemsWritten: 1, // .dirmeta for collection itemsWritten: 1, // .dirmeta for collection
@ -1611,10 +1609,6 @@ func runDriveIncrementalTest(
} }
for _, test := range table { for _, test := range table {
suite.Run(test.name, func() { suite.Run(test.name, func() {
if test.skip {
suite.T().Skip("flagged to skip")
}
cleanGC, err := connector.NewGraphConnector(ctx, acct, resource) cleanGC, err := connector.NewGraphConnector(ctx, acct, resource)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))

View File

@ -226,11 +226,9 @@ func (dm DetailsModel) FilterMetaFiles() DetailsModel {
} }
// Check if a file is a metadata file. These are used to store // Check if a file is a metadata file. These are used to store
// additional data like permissions in case of OneDrive and are not to // additional data like permissions (in case of drive items) and are not to
// be treated as regular files. // be treated as regular files.
func (de Entry) isMetaFile() bool { func (de Entry) isMetaFile() bool {
// TODO: Add meta file filtering to SharePoint as well once we add
// meta files for SharePoint.
return de.ItemInfo.OneDrive != nil && de.ItemInfo.OneDrive.IsMeta return de.ItemInfo.OneDrive != nil && de.ItemInfo.OneDrive.IsMeta
} }
@ -522,9 +520,9 @@ func (de Entry) ToLocationIDer(backupVersion int) (LocationIDer, error) {
return nil, clues.Wrap(err, "getting item RepoRef") return nil, clues.Wrap(err, "getting item RepoRef")
} }
p, err := path.ToOneDrivePath(rr) p, err := path.ToDrivePath(rr)
if err != nil { if err != nil {
return nil, clues.New("converting RepoRef to OneDrive path") return nil, clues.New("converting RepoRef to drive path")
} }
baseLoc := path.Builder{}.Append(p.Root).Append(p.Folders...) baseLoc := path.Builder{}.Append(p.Root).Append(p.Folders...)

View File

@ -8,19 +8,21 @@ import "github.com/alcionai/clues"
// //
// driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and // driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and
// folders[] is []{"Folder1", "Folder2"} // folders[] is []{"Folder1", "Folder2"}
//
// Should be compatible with all drive-based services (ex: oneDrive, sharePoint Libraries, etc)
type DrivePath struct { type DrivePath struct {
DriveID string DriveID string
Root string Root string
Folders Elements Folders Elements
} }
func ToOneDrivePath(p Path) (*DrivePath, error) { func ToDrivePath(p Path) (*DrivePath, error) {
folders := p.Folders() folders := p.Folders()
// Must be at least `drives/<driveID>/root:` // Must be at least `drives/<driveID>/root:`
if len(folders) < 3 { if len(folders) < 3 {
return nil, clues. return nil, clues.
New("folder path doesn't match expected format for OneDrive items"). New("folder path doesn't match expected format for Drive items").
With("path_folders", p.Folder(false)) With("path_folders", p.Folder(false))
} }
@ -29,7 +31,7 @@ func ToOneDrivePath(p Path) (*DrivePath, error) {
// Returns the path to the folder within the drive (i.e. under `root:`) // Returns the path to the folder within the drive (i.e. under `root:`)
func GetDriveFolderPath(p Path) (string, error) { func GetDriveFolderPath(p Path) (string, error) {
drivePath, err := ToOneDrivePath(p) drivePath, err := ToDrivePath(p)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -54,7 +54,7 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() {
p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...) p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...)
require.NoError(suite.T(), err, clues.ToCore(err)) require.NoError(suite.T(), err, clues.ToCore(err))
got, err := path.ToOneDrivePath(p) got, err := path.ToDrivePath(p)
tt.errCheck(t, err) tt.errCheck(t, err)
if err != nil { if err != nil {
return return