Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79a7288d99 | ||
|
|
45c40993d4 | ||
|
|
5754bbaef0 | ||
|
|
b589490f11 | ||
|
|
95e1af794d |
@ -49,10 +49,10 @@ const (
|
||||
oneDriveServiceCommandRestoreExamples = `# Restore file with ID 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
|
||||
|
||||
# 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 \
|
||||
--user alice@example.com --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports"
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
utils.AddBackupIDFlag(c, true)
|
||||
utils.AddSharePointDetailsAndRestoreFlags(c)
|
||||
|
||||
options.AddRestorePermissionsFlag(c)
|
||||
options.AddFailFastFlag(c)
|
||||
}
|
||||
|
||||
@ -47,7 +49,11 @@ const (
|
||||
sharePointServiceCommandRestoreExamples = `# Restore file with ID 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 \
|
||||
--file "ServerRenderTemplate.xsl" --folder "Display Templates/Style Sheets"
|
||||
|
||||
|
||||
2
src/cli/utils/testdata/flags.go
vendored
2
src/cli/utils/testdata/flags.go
vendored
@ -43,4 +43,6 @@ var (
|
||||
|
||||
PageFolderInput = []string{"pageFolder1", "pageFolder2"}
|
||||
PageInput = []string{"page1", "page2"}
|
||||
|
||||
RestorePermissions = true
|
||||
)
|
||||
|
||||
@ -218,7 +218,7 @@ func (gc *GraphConnector) ConsumeRestoreCollections(
|
||||
case selectors.ServiceOneDrive:
|
||||
status, err = onedrive.RestoreCollections(ctx, creds, backupVersion, gc.Service, dest, opts, dcs, deets, errs)
|
||||
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:
|
||||
err = clues.Wrap(clues.New(selector.Service.String()), "service not supported")
|
||||
}
|
||||
|
||||
@ -228,10 +228,6 @@ func (m *SiteItemRequestBuilder) Patch(
|
||||
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.
|
||||
//
|
||||
//nolint:revive,wsl
|
||||
|
||||
@ -722,14 +722,14 @@ func permissionEqual(expected metadata.Permission, got metadata.Permission) bool
|
||||
return true
|
||||
}
|
||||
|
||||
func compareOneDriveItem(
|
||||
func compareDriveItem(
|
||||
t *testing.T,
|
||||
expected map[string][]byte,
|
||||
item data.Stream,
|
||||
restorePermissions bool,
|
||||
rootDir 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.
|
||||
if rootDir && item.UUID() == metadata.DirMetaFileSuffix {
|
||||
return false
|
||||
@ -747,7 +747,7 @@ func compareOneDriveItem(
|
||||
)
|
||||
|
||||
if isMeta {
|
||||
var itemType *onedrive.MetadataItem
|
||||
var itemType *metadata.Item
|
||||
|
||||
assert.IsType(t, itemType, item)
|
||||
} else {
|
||||
@ -824,8 +824,7 @@ func compareOneDriveItem(
|
||||
t,
|
||||
expectedMeta.Permissions,
|
||||
itemPerms,
|
||||
permissionEqual,
|
||||
)
|
||||
permissionEqual)
|
||||
|
||||
return true
|
||||
}
|
||||
@ -887,7 +886,7 @@ func compareItem(
|
||||
}
|
||||
|
||||
case path.OneDriveService:
|
||||
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||
return compareDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||
|
||||
case path.SharePointService:
|
||||
if category != path.LibrariesCategory {
|
||||
@ -895,7 +894,7 @@ func compareItem(
|
||||
}
|
||||
|
||||
// SharePoint libraries reuses OneDrive code.
|
||||
return compareOneDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||
return compareDriveItem(t, expected, item, restorePermissions, rootDir)
|
||||
|
||||
default:
|
||||
assert.FailNowf(t, "unexpected service: %s", service.String())
|
||||
|
||||
@ -431,12 +431,6 @@ func (si suiteInfoImpl) Resource() Resource {
|
||||
// SharePoint shares most of its libraries implementation with OneDrive so we
|
||||
// only test simple things here and leave the more extensive testing to
|
||||
// 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 {
|
||||
tester.Suite
|
||||
suiteInfo
|
||||
@ -486,6 +480,18 @@ func (suite *GraphConnectorSharePointIntegrationSuite) TestRestoreAndBackup_Mult
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -372,3 +372,21 @@ func GetFolderByName(
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -37,8 +37,8 @@ var (
|
||||
_ data.Stream = &Item{}
|
||||
_ data.StreamInfo = &Item{}
|
||||
_ data.StreamModTime = &Item{}
|
||||
_ data.Stream = &MetadataItem{}
|
||||
_ data.StreamModTime = &MetadataItem{}
|
||||
_ data.Stream = &metadata.Item{}
|
||||
_ data.StreamModTime = &metadata.Item{}
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
odp, err := path.ToOneDrivePath(p)
|
||||
dp, err := path.ToDrivePath(p)
|
||||
if err != nil {
|
||||
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
|
||||
@ -306,53 +306,14 @@ type Item struct {
|
||||
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
|
||||
// as deleted by adding them to the exclude list so this can always return
|
||||
// false.
|
||||
func (od Item) Deleted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (od *Item) Info() details.ItemInfo {
|
||||
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
|
||||
}
|
||||
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) Info() details.ItemInfo { return i.info }
|
||||
func (i *Item) ModTime() time.Time { return i.info.Modified() }
|
||||
|
||||
// getDriveItemContent fetch drive item's contents with retries
|
||||
func (oc *Collection) getDriveItemContent(
|
||||
@ -602,12 +563,12 @@ func (oc *Collection) populateItems(ctx context.Context, errs *fault.Bus) {
|
||||
return progReader, nil
|
||||
})
|
||||
|
||||
oc.data <- &MetadataItem{
|
||||
id: metaFileName + metaSuffix,
|
||||
data: metaReader,
|
||||
oc.data <- &metadata.Item{
|
||||
ID: metaFileName + metaSuffix,
|
||||
Data: metaReader,
|
||||
// Metadata file should always use the latest time as
|
||||
// permissions change does not update mod time.
|
||||
modTime: time.Now(),
|
||||
Mod: time.Now(),
|
||||
}
|
||||
|
||||
// Item read successfully, add to collection
|
||||
|
||||
@ -18,7 +18,9 @@ import (
|
||||
"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/mock"
|
||||
"github.com/alcionai/corso/src/internal/connector/onedrive/metadata"
|
||||
"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/fault"
|
||||
"github.com/alcionai/corso/src/pkg/logger"
|
||||
@ -281,6 +283,7 @@ func (suite *OneDriveUnitSuite) TestDrives() {
|
||||
type OneDriveSuite struct {
|
||||
tester.Suite
|
||||
userID string
|
||||
creds account.M365Config
|
||||
}
|
||||
|
||||
func TestOneDriveSuite(t *testing.T) {
|
||||
@ -292,7 +295,15 @@ func TestOneDriveSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -333,17 +344,46 @@ func (suite *OneDriveSuite) TestCreateGetDeleteFolder() {
|
||||
rootFolder, err := api.GetDriveRoot(ctx, gs, driveID)
|
||||
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))
|
||||
|
||||
folderIDs = append(folderIDs, folderID)
|
||||
|
||||
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))
|
||||
|
||||
folderIDs = append(folderIDs, folderID)
|
||||
|
||||
@ -63,7 +63,6 @@ func sharePointItemMetaReader(
|
||||
driveID string,
|
||||
item models.DriveItemable,
|
||||
) (io.ReadCloser, int, error) {
|
||||
// TODO: include permissions
|
||||
return baseItemMetaReader(ctx, service, driveID, item)
|
||||
}
|
||||
|
||||
@ -243,23 +242,23 @@ func filterUserPermissions(ctx context.Context, perms []models.Permissionable) [
|
||||
// read - Read
|
||||
// empty - Restricted View
|
||||
roles := p.GetRoles()
|
||||
|
||||
entityID := ""
|
||||
if gv2.GetUser() != nil {
|
||||
|
||||
switch true {
|
||||
case gv2.GetUser() != nil:
|
||||
entityID = ptr.Val(gv2.GetUser().GetId())
|
||||
} else if gv2.GetGroup() != nil {
|
||||
case gv2.GetGroup() != nil:
|
||||
entityID = ptr.Val(gv2.GetGroup().GetId())
|
||||
} else {
|
||||
// TODO Add application permissions when adding permissions for SharePoint
|
||||
// https://devblogs.microsoft.com/microsoft365dev/controlling-app-access-on-specific-sharepoint-site-collections/
|
||||
logm := logger.Ctx(ctx)
|
||||
if gv2.GetApplication() != nil {
|
||||
logm.With("application_id", ptr.Val(gv2.GetApplication().GetId()))
|
||||
}
|
||||
if gv2.GetDevice() != nil {
|
||||
logm.With("device_id", ptr.Val(gv2.GetDevice().GetId()))
|
||||
}
|
||||
logm.Info("untracked permission")
|
||||
case gv2.GetApplication() != nil:
|
||||
entityID = ptr.Val(gv2.GetApplication().GetId())
|
||||
case gv2.GetDevice() != nil:
|
||||
entityID = ptr.Val(gv2.GetDevice().GetId())
|
||||
default:
|
||||
logger.Ctx(ctx).Info("untracked permission")
|
||||
}
|
||||
|
||||
if gv2.GetDevice() != nil {
|
||||
logger.Ctx(ctx).With("device_id", ptr.Val(gv2.GetDevice().GetId()))
|
||||
}
|
||||
|
||||
// Technically GrantedToV2 can also contain devices, but the
|
||||
|
||||
@ -288,7 +288,7 @@ func TestItemUnitTestSuite(t *testing.T) {
|
||||
suite.Run(t, &ItemUnitTestSuite{Suite: tester.NewUnitSuite(t)})
|
||||
}
|
||||
|
||||
func (suite *ItemUnitTestSuite) TestOneDrivePermissionsFilter() {
|
||||
func (suite *ItemUnitTestSuite) TestDrivePermissionsFilter() {
|
||||
permID := "fakePermId"
|
||||
userID := "fakeuser@provider.com"
|
||||
userID2 := "fakeuser2@provider.com"
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ItemMeta contains metadata about the Item. It gets stored in a
|
||||
// separate file in kopia
|
||||
type Metadata struct {
|
||||
@ -10,3 +15,17 @@ type Metadata struct {
|
||||
SharingMode SharingMode `json:"permissionMode,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 }
|
||||
|
||||
@ -2,13 +2,15 @@ package onedrive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/alcionai/corso/src/internal/common/ptr"
|
||||
"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/data"
|
||||
"github.com/alcionai/corso/src/internal/version"
|
||||
@ -22,12 +24,12 @@ func getParentMetadata(
|
||||
) (metadata.Metadata, error) {
|
||||
parentMeta, ok := metas[parentPath.String()]
|
||||
if !ok {
|
||||
onedrivePath, err := path.ToOneDrivePath(parentPath)
|
||||
drivePath, err := path.ToDrivePath(parentPath)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -41,7 +43,7 @@ func getCollectionMetadata(
|
||||
ctx context.Context,
|
||||
drivePath *path.DrivePath,
|
||||
dc data.RestoreCollection,
|
||||
metas map[string]metadata.Metadata,
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
backupVersion int,
|
||||
restorePerms bool,
|
||||
) (metadata.Metadata, error) {
|
||||
@ -59,12 +61,16 @@ func getCollectionMetadata(
|
||||
return metadata.Metadata{}, nil
|
||||
}
|
||||
|
||||
fmt.Printf("\n-----\nbackupVersion %+v\n-----\n", backupVersion)
|
||||
|
||||
if backupVersion < version.OneDrive4DirIncludesPermissions {
|
||||
colMeta, err := getParentMetadata(collectionPath, metas)
|
||||
colMeta, err := getParentMetadata(collectionPath, parentDirToMeta)
|
||||
if err != nil {
|
||||
return metadata.Metadata{}, clues.Wrap(err, "collection metadata")
|
||||
}
|
||||
|
||||
fmt.Printf("\n-----\ngot colMeta %+v\n-----\n", colMeta)
|
||||
|
||||
return colMeta, nil
|
||||
}
|
||||
|
||||
@ -88,7 +94,11 @@ func getCollectionMetadata(
|
||||
// traversing folderMetas and finding the first item with custom
|
||||
// permissions. folderMetas is expected to have all the parent
|
||||
// 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 (
|
||||
parent path.Path
|
||||
meta metadata.Metadata
|
||||
@ -97,7 +107,7 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat
|
||||
ok bool
|
||||
)
|
||||
|
||||
parent = itemPath
|
||||
parent = originDir
|
||||
|
||||
for {
|
||||
parent, err = parent.Dir()
|
||||
@ -105,21 +115,24 @@ func computeParentPermissions(itemPath path.Path, folderMetas map[string]metadat
|
||||
return metadata.Metadata{}, clues.New("getting parent")
|
||||
}
|
||||
|
||||
onedrivePath, err := path.ToOneDrivePath(parent)
|
||||
drivePath, err := path.ToDrivePath(parent)
|
||||
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
|
||||
}
|
||||
|
||||
meta, ok = folderMetas[parent.String()]
|
||||
meta, ok = parentDirToMeta[parent.String()]
|
||||
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 {
|
||||
fmt.Printf("\n-----\nbefore return %+v\n-----\n", parentDirToMeta)
|
||||
return meta, nil
|
||||
}
|
||||
}
|
||||
@ -134,7 +147,7 @@ func UpdatePermissions(
|
||||
driveID string,
|
||||
itemID string,
|
||||
permAdded, permRemoved []metadata.Permission,
|
||||
permissionIDMappings map[string]string,
|
||||
oldPermIDToNewID map[string]string,
|
||||
) error {
|
||||
// The ordering of the operations is important here. We first
|
||||
// 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")
|
||||
}
|
||||
|
||||
pid, ok := permissionIDMappings[p.ID]
|
||||
pid, ok := oldPermIDToNewID[p.ID]
|
||||
if !ok {
|
||||
return clues.New("no new permission id").WithClues(ctx)
|
||||
}
|
||||
@ -179,7 +192,7 @@ func UpdatePermissions(
|
||||
continue
|
||||
}
|
||||
|
||||
pbody := msdrive.NewItemsItemInvitePostRequestBody()
|
||||
pbody := drive.NewItemsItemInvitePostRequestBody()
|
||||
pbody.SetRoles(roles)
|
||||
|
||||
if p.Expiration != nil {
|
||||
@ -204,12 +217,12 @@ func UpdatePermissions(
|
||||
|
||||
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 {
|
||||
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
|
||||
@ -225,23 +238,28 @@ func RestorePermissions(
|
||||
service graph.Servicer,
|
||||
driveID string,
|
||||
itemID string,
|
||||
itemPath path.Path,
|
||||
meta metadata.Metadata,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
permissionIDMappings map[string]string,
|
||||
originDir path.Path,
|
||||
current metadata.Metadata,
|
||||
// map parent dir -> parent's metadata
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
oldPermIDToNewID map[string]string,
|
||||
) error {
|
||||
if meta.SharingMode == metadata.SharingModeInherited {
|
||||
if current.SharingMode == metadata.SharingModeInherited {
|
||||
return nil
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -22,28 +22,39 @@ func TestPermissionsUnitTestSuite(t *testing.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"
|
||||
rootEntryPath := fmt.Sprintf(rootDrivePattern, "drive-id") + "/entry"
|
||||
|
||||
entry, err := path.Build(
|
||||
"tenant",
|
||||
"user",
|
||||
path.OneDriveService,
|
||||
path.FilesCategory,
|
||||
resourceOwner,
|
||||
service,
|
||||
category,
|
||||
false,
|
||||
strings.Split(entryPath, "/")...,
|
||||
)
|
||||
strings.Split(entryPath, "/")...)
|
||||
require.NoError(suite.T(), err, "creating path")
|
||||
|
||||
rootEntry, err := path.Build(
|
||||
"tenant",
|
||||
"user",
|
||||
path.OneDriveService,
|
||||
path.FilesCategory,
|
||||
resourceOwner,
|
||||
service,
|
||||
category,
|
||||
false,
|
||||
strings.Split(rootEntryPath, "/")...,
|
||||
)
|
||||
strings.Split(rootEntryPath, "/")...)
|
||||
require.NoError(suite.T(), err, "creating path")
|
||||
|
||||
level2, err := entry.Dir()
|
||||
|
||||
@ -3,6 +3,7 @@ package onedrive
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/trace"
|
||||
"sort"
|
||||
@ -46,15 +47,15 @@ func RestoreCollections(
|
||||
errs *fault.Bus,
|
||||
) (*support.ConnectorOperationStatus, error) {
|
||||
var (
|
||||
restoreMetrics support.CollectionMetrics
|
||||
metrics support.CollectionMetrics
|
||||
folderMetas = map[string]metadata.Metadata{}
|
||||
restoreMetrics support.CollectionMetrics
|
||||
metrics support.CollectionMetrics
|
||||
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
|
||||
permissionIDMappings = map[string]string{}
|
||||
fc = NewFolderCache()
|
||||
rootIDCache = map[string]string{}
|
||||
oldPermIDToNewID = map[string]string{}
|
||||
fc = NewFolderCache()
|
||||
rootFolderIDCache = map[string]string{}
|
||||
)
|
||||
|
||||
ctx = clues.Add(
|
||||
@ -91,10 +92,10 @@ func RestoreCollections(
|
||||
backupVersion,
|
||||
service,
|
||||
dc,
|
||||
folderMetas,
|
||||
permissionIDMappings,
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID,
|
||||
fc,
|
||||
rootIDCache,
|
||||
rootFolderIDCache,
|
||||
OneDriveSource,
|
||||
dest.ContainerName,
|
||||
deets,
|
||||
@ -132,10 +133,12 @@ func RestoreCollection(
|
||||
backupVersion int,
|
||||
service graph.Servicer,
|
||||
dc data.RestoreCollection,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
permissionIDMappings map[string]string,
|
||||
// cache of parent dir -> parent's metadata,
|
||||
// mutated during this call
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
oldPermIDToNewID map[string]string,
|
||||
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,
|
||||
restoreContainerName string,
|
||||
deets *details.Builder,
|
||||
@ -152,69 +155,75 @@ func RestoreCollection(
|
||||
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
|
||||
defer end()
|
||||
|
||||
drivePath, err := path.ToOneDrivePath(directory)
|
||||
drivePath, err := path.ToDrivePath(directory)
|
||||
if err != nil {
|
||||
return metrics, clues.Wrap(err, "creating drive path").WithClues(ctx)
|
||||
}
|
||||
|
||||
if rootIDCache == nil {
|
||||
rootIDCache = map[string]string{}
|
||||
if rootFolderIDCache == nil {
|
||||
rootFolderIDCache = map[string]string{}
|
||||
}
|
||||
|
||||
if _, ok := rootIDCache[drivePath.DriveID]; !ok {
|
||||
if _, ok := rootFolderIDCache[drivePath.DriveID]; !ok {
|
||||
root, err := api.GetDriveRoot(ctx, service, drivePath.DriveID)
|
||||
if err != nil {
|
||||
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
|
||||
// from the backup under this the restore folder instead of root)
|
||||
// i.e. Restore into `<restoreContainerName>/<original folder path>`
|
||||
// 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,
|
||||
"directory", dc.FullPath().Folder(false),
|
||||
"destination_elements", restoreFolderElements,
|
||||
"destination_dir", restoreDir,
|
||||
"drive_id", drivePath.DriveID)
|
||||
|
||||
trace.Log(ctx, "gc:oneDrive:restoreCollection", directory.String())
|
||||
logger.Ctx(ctx).Info("restoring onedrive collection")
|
||||
|
||||
fmt.Printf("\n-----\n1 %+v\n-----\n", parentDirToMeta)
|
||||
|
||||
colMeta, err := getCollectionMetadata(
|
||||
ctx,
|
||||
drivePath,
|
||||
dc,
|
||||
folderMetas,
|
||||
parentDirToMeta,
|
||||
backupVersion,
|
||||
restorePerms)
|
||||
if err != nil {
|
||||
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
|
||||
restoreFolderID, err := createRestoreFoldersWithPermissions(
|
||||
restoreFolderID, err := CreateRestoreFolders(
|
||||
ctx,
|
||||
creds,
|
||||
service,
|
||||
drivePath,
|
||||
rootIDCache[drivePath.DriveID],
|
||||
restoreFolderElements,
|
||||
rootFolderIDCache[drivePath.DriveID],
|
||||
restoreDir,
|
||||
dc.FullPath(),
|
||||
colMeta,
|
||||
folderMetas,
|
||||
parentDirToMeta,
|
||||
fc,
|
||||
permissionIDMappings,
|
||||
oldPermIDToNewID,
|
||||
restorePerms)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
for {
|
||||
@ -247,8 +256,8 @@ func RestoreCollection(
|
||||
drivePath,
|
||||
restoreFolderID,
|
||||
copyBuffer,
|
||||
folderMetas,
|
||||
permissionIDMappings,
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID,
|
||||
restorePerms,
|
||||
itemData,
|
||||
itemPath)
|
||||
@ -298,8 +307,8 @@ func restoreItem(
|
||||
drivePath *path.DrivePath,
|
||||
restoreFolderID string,
|
||||
copyBuffer []byte,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
permissionIDMappings map[string]string,
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
oldPermIDToNewID map[string]string,
|
||||
restorePerms bool,
|
||||
itemData data.Stream,
|
||||
itemPath path.Path,
|
||||
@ -348,7 +357,7 @@ func restoreItem(
|
||||
}
|
||||
|
||||
trimmedPath := strings.TrimSuffix(itemPath.String(), metadata.DirMetaFileSuffix)
|
||||
folderMetas[trimmedPath] = meta
|
||||
parentDirToMeta[trimmedPath] = meta
|
||||
|
||||
return details.ItemInfo{}, true, nil
|
||||
}
|
||||
@ -366,8 +375,8 @@ func restoreItem(
|
||||
restoreFolderID,
|
||||
copyBuffer,
|
||||
restorePerms,
|
||||
folderMetas,
|
||||
permissionIDMappings,
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID,
|
||||
itemPath,
|
||||
itemData)
|
||||
if err != nil {
|
||||
@ -389,8 +398,8 @@ func restoreItem(
|
||||
restoreFolderID,
|
||||
copyBuffer,
|
||||
restorePerms,
|
||||
folderMetas,
|
||||
permissionIDMappings,
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID,
|
||||
itemPath,
|
||||
itemData)
|
||||
if err != nil {
|
||||
@ -439,8 +448,8 @@ func restoreV1File(
|
||||
restoreFolderID string,
|
||||
copyBuffer []byte,
|
||||
restorePerms bool,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
permissionIDMappings map[string]string,
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
oldPermIDToNewID map[string]string,
|
||||
itemPath path.Path,
|
||||
itemData data.Stream,
|
||||
) (details.ItemInfo, error) {
|
||||
@ -481,8 +490,8 @@ func restoreV1File(
|
||||
itemID,
|
||||
itemPath,
|
||||
meta,
|
||||
folderMetas,
|
||||
permissionIDMappings)
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID)
|
||||
if err != nil {
|
||||
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
||||
}
|
||||
@ -500,8 +509,8 @@ func restoreV6File(
|
||||
restoreFolderID string,
|
||||
copyBuffer []byte,
|
||||
restorePerms bool,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
permissionIDMappings map[string]string,
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
oldPermIDToNewID map[string]string,
|
||||
itemPath path.Path,
|
||||
itemData data.Stream,
|
||||
) (details.ItemInfo, error) {
|
||||
@ -553,8 +562,8 @@ func restoreV6File(
|
||||
itemID,
|
||||
itemPath,
|
||||
meta,
|
||||
folderMetas,
|
||||
permissionIDMappings)
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID)
|
||||
if err != nil {
|
||||
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
||||
}
|
||||
@ -562,31 +571,31 @@ func restoreV6File(
|
||||
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
|
||||
// hierarchy. Permissions are only applied to the last folder in the hierarchy.
|
||||
// Passing nil for the permissions results in just creating the folder(s).
|
||||
// folderCache is mutated, as a side effect of populating the items.
|
||||
func createRestoreFoldersWithPermissions(
|
||||
func CreateRestoreFolders(
|
||||
ctx context.Context,
|
||||
creds account.M365Config,
|
||||
service graph.Servicer,
|
||||
drivePath *path.DrivePath,
|
||||
driveRootID string,
|
||||
restoreFolders *path.Builder,
|
||||
folderPath path.Path,
|
||||
folderMetadata metadata.Metadata,
|
||||
folderMetas map[string]metadata.Metadata,
|
||||
driveRootFolderID string,
|
||||
restoreDir *path.Builder,
|
||||
originDir path.Path,
|
||||
colMeta metadata.Metadata,
|
||||
parentDirToMeta map[string]metadata.Metadata,
|
||||
fc *folderCache,
|
||||
permissionIDMappings map[string]string,
|
||||
oldPermIDToNewID map[string]string,
|
||||
restorePerms bool,
|
||||
) (string, error) {
|
||||
id, err := CreateRestoreFolders(
|
||||
id, err := createRestoreFolders(
|
||||
ctx,
|
||||
service,
|
||||
drivePath.DriveID,
|
||||
driveRootID,
|
||||
restoreFolders,
|
||||
driveRootFolderID,
|
||||
restoreDir,
|
||||
fc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -607,28 +616,28 @@ func createRestoreFoldersWithPermissions(
|
||||
service,
|
||||
drivePath.DriveID,
|
||||
id,
|
||||
folderPath,
|
||||
folderMetadata,
|
||||
folderMetas,
|
||||
permissionIDMappings)
|
||||
originDir,
|
||||
colMeta,
|
||||
parentDirToMeta,
|
||||
oldPermIDToNewID)
|
||||
|
||||
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.
|
||||
// folderCache is mutated, as a side effect of populating the items.
|
||||
func CreateRestoreFolders(
|
||||
func createRestoreFolders(
|
||||
ctx context.Context,
|
||||
service graph.Servicer,
|
||||
driveID, driveRootID string,
|
||||
restoreFolders *path.Builder,
|
||||
restoreDir *path.Builder,
|
||||
fc *folderCache,
|
||||
) (string, error) {
|
||||
var (
|
||||
location = &path.Builder{}
|
||||
parentFolderID = driveRootID
|
||||
folders = restoreFolders.Elements()
|
||||
folders = restoreDir.Elements()
|
||||
)
|
||||
|
||||
for _, folder := range folders {
|
||||
@ -791,12 +800,12 @@ func AugmentRestorePaths(backupVersion int, paths []path.Path) ([]path.Path, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
onedrivePath, err := path.ToOneDrivePath(np)
|
||||
drivePath, err := path.ToDrivePath(np)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(onedrivePath.Folders) == 0 {
|
||||
if len(drivePath.Folders) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@ func (ms *MockGraphService) Adapter() *msgraphsdk.GraphRequestAdapter {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ graph.Servicer = &oneDriveService{}
|
||||
|
||||
// TODO(ashmrtn): Merge with similar structs in graph and exchange packages.
|
||||
type oneDriveService struct {
|
||||
client msgraphsdk.GraphServiceClient
|
||||
|
||||
@ -86,9 +86,7 @@ func DataCollections(
|
||||
}
|
||||
|
||||
case path.LibrariesCategory:
|
||||
var excludes map[string]map[string]struct{}
|
||||
|
||||
spcs, excludes, err = collectLibraries(
|
||||
spcs, excluded, err = collectLibraries(
|
||||
ctx,
|
||||
itemClient,
|
||||
serv,
|
||||
@ -104,7 +102,7 @@ func DataCollections(
|
||||
continue
|
||||
}
|
||||
|
||||
for prefix, excludes := range excludes {
|
||||
for prefix, excludes := range excluded {
|
||||
if _, ok := excluded[prefix]; !ok {
|
||||
excluded[prefix] = map[string]struct{}{}
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ func RestoreCollections(
|
||||
creds account.M365Config,
|
||||
service graph.Servicer,
|
||||
dest control.RestoreDestination,
|
||||
opts control.Options,
|
||||
dcs []data.RestoreCollection,
|
||||
deets *details.Builder,
|
||||
errs *fault.Bus,
|
||||
@ -82,7 +83,7 @@ func RestoreCollections(
|
||||
onedrive.SharePointSource,
|
||||
dest.ContainerName,
|
||||
deets,
|
||||
false,
|
||||
opts.RestorePermissions,
|
||||
errs)
|
||||
|
||||
case path.ListsCategory:
|
||||
|
||||
@ -386,13 +386,16 @@ func generateContainerOfItems(
|
||||
dest,
|
||||
collections)
|
||||
|
||||
opts := control.Defaults()
|
||||
opts.RestorePermissions = true
|
||||
|
||||
deets, err := gc.ConsumeRestoreCollections(
|
||||
ctx,
|
||||
backupVersion,
|
||||
acct,
|
||||
sel,
|
||||
dest,
|
||||
control.Options{RestorePermissions: true},
|
||||
opts,
|
||||
dataColls,
|
||||
fault.New(true))
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
@ -1333,7 +1336,6 @@ func runDriveIncrementalTest(
|
||||
updateFiles func(t *testing.T)
|
||||
itemsRead int
|
||||
itemsWritten int
|
||||
skip bool
|
||||
}{
|
||||
{
|
||||
name: "clean incremental, no changes",
|
||||
@ -1361,7 +1363,6 @@ func runDriveIncrementalTest(
|
||||
},
|
||||
{
|
||||
name: "add permission to new file",
|
||||
skip: skipPermissionsTests,
|
||||
updateFiles: func(t *testing.T) {
|
||||
driveItem := models.NewDriveItem()
|
||||
driveItem.SetName(&newFileName)
|
||||
@ -1382,7 +1383,6 @@ func runDriveIncrementalTest(
|
||||
},
|
||||
{
|
||||
name: "remove permission from new file",
|
||||
skip: skipPermissionsTests,
|
||||
updateFiles: func(t *testing.T) {
|
||||
driveItem := models.NewDriveItem()
|
||||
driveItem.SetName(&newFileName)
|
||||
@ -1396,14 +1396,13 @@ func runDriveIncrementalTest(
|
||||
[]metadata.Permission{},
|
||||
[]metadata.Permission{writePerm},
|
||||
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
|
||||
itemsWritten: 2, // .meta for newitem, .dirmeta for parent (.data is not written as it is not updated)
|
||||
},
|
||||
{
|
||||
name: "add permission to container",
|
||||
skip: skipPermissionsTests,
|
||||
updateFiles: func(t *testing.T) {
|
||||
targetContainer := containerIDs[container1]
|
||||
driveItem := models.NewDriveItem()
|
||||
@ -1418,14 +1417,13 @@ func runDriveIncrementalTest(
|
||||
[]metadata.Permission{writePerm},
|
||||
[]metadata.Permission{},
|
||||
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,
|
||||
itemsWritten: 1, // .dirmeta for collection
|
||||
},
|
||||
{
|
||||
name: "remove permission from container",
|
||||
skip: skipPermissionsTests,
|
||||
updateFiles: func(t *testing.T) {
|
||||
targetContainer := containerIDs[container1]
|
||||
driveItem := models.NewDriveItem()
|
||||
@ -1440,7 +1438,7 @@ func runDriveIncrementalTest(
|
||||
[]metadata.Permission{},
|
||||
[]metadata.Permission{writePerm},
|
||||
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,
|
||||
itemsWritten: 1, // .dirmeta for collection
|
||||
@ -1611,10 +1609,6 @@ func runDriveIncrementalTest(
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
if test.skip {
|
||||
suite.T().Skip("flagged to skip")
|
||||
}
|
||||
|
||||
cleanGC, err := connector.NewGraphConnector(ctx, acct, resource)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
|
||||
@ -226,11 +226,9 @@ func (dm DetailsModel) FilterMetaFiles() DetailsModel {
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
|
||||
@ -522,9 +520,9 @@ func (de Entry) ToLocationIDer(backupVersion int) (LocationIDer, error) {
|
||||
return nil, clues.Wrap(err, "getting item RepoRef")
|
||||
}
|
||||
|
||||
p, err := path.ToOneDrivePath(rr)
|
||||
p, err := path.ToDrivePath(rr)
|
||||
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...)
|
||||
|
||||
@ -8,19 +8,21 @@ import "github.com/alcionai/clues"
|
||||
//
|
||||
// driveID is `b!X_8Z2zuXpkKkXZsr7gThk9oJpuj0yXVGnK5_VjRRPK-q725SX_8ZQJgFDK8PlFxA` and
|
||||
// folders[] is []{"Folder1", "Folder2"}
|
||||
//
|
||||
// Should be compatible with all drive-based services (ex: oneDrive, sharePoint Libraries, etc)
|
||||
type DrivePath struct {
|
||||
DriveID string
|
||||
Root string
|
||||
Folders Elements
|
||||
}
|
||||
|
||||
func ToOneDrivePath(p Path) (*DrivePath, error) {
|
||||
func ToDrivePath(p Path) (*DrivePath, error) {
|
||||
folders := p.Folders()
|
||||
|
||||
// Must be at least `drives/<driveID>/root:`
|
||||
if len(folders) < 3 {
|
||||
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))
|
||||
}
|
||||
|
||||
@ -29,7 +31,7 @@ func ToOneDrivePath(p Path) (*DrivePath, error) {
|
||||
|
||||
// Returns the path to the folder within the drive (i.e. under `root:`)
|
||||
func GetDriveFolderPath(p Path) (string, error) {
|
||||
drivePath, err := ToOneDrivePath(p)
|
||||
drivePath, err := ToDrivePath(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -54,7 +54,7 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() {
|
||||
p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...)
|
||||
require.NoError(suite.T(), err, clues.ToCore(err))
|
||||
|
||||
got, err := path.ToOneDrivePath(p)
|
||||
got, err := path.ToDrivePath(p)
|
||||
tt.errCheck(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
Loading…
x
Reference in New Issue
Block a user