permission basics
this doesn't solve all permission handling in sharepoint, but it sets a lot of groundwork that needs to be done anyway.
This commit is contained in:
parent
7326730e0d
commit
5a7e02ea31
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
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"}
|
PageFolderInput = []string{"pageFolder1", "pageFolder2"}
|
||||||
PageInput = []string{"page1", "page2"}
|
PageInput = []string{"page1", "page2"}
|
||||||
|
|
||||||
|
RestorePermissions = true
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,11 +249,12 @@ func filterUserPermissions(ctx context.Context, perms []models.Permissionable) [
|
|||||||
} else if gv2.GetGroup() != nil {
|
} else if gv2.GetGroup() != nil {
|
||||||
entityID = ptr.Val(gv2.GetGroup().GetId())
|
entityID = ptr.Val(gv2.GetGroup().GetId())
|
||||||
} else {
|
} 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)
|
logm := logger.Ctx(ctx)
|
||||||
|
|
||||||
if gv2.GetApplication() != nil {
|
if gv2.GetApplication() != nil {
|
||||||
logm.With("application_id", ptr.Val(gv2.GetApplication().GetId()))
|
entityID = ptr.Val(gv2.GetApplication().GetId())
|
||||||
|
} else if gv2.GetDevice() != nil {
|
||||||
|
entityID = ptr.Val(gv2.GetDevice().GetId())
|
||||||
}
|
}
|
||||||
if gv2.GetDevice() != nil {
|
if gv2.GetDevice() != nil {
|
||||||
logm.With("device_id", ptr.Val(gv2.GetDevice().GetId()))
|
logm.With("device_id", ptr.Val(gv2.GetDevice().GetId()))
|
||||||
|
|||||||
@ -89,8 +89,9 @@ func getCollectionMetadata(
|
|||||||
// 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(
|
func computeParentPermissions(
|
||||||
itemPath path.Path,
|
originDir path.Path,
|
||||||
folderMetas map[string]metadata.Metadata,
|
// map parent dir -> parent's metadata
|
||||||
|
parentMetas map[string]metadata.Metadata,
|
||||||
) (metadata.Metadata, error) {
|
) (metadata.Metadata, error) {
|
||||||
var (
|
var (
|
||||||
parent path.Path
|
parent path.Path
|
||||||
@ -100,7 +101,7 @@ func computeParentPermissions(
|
|||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
|
|
||||||
parent = itemPath
|
parent = originDir
|
||||||
|
|
||||||
for {
|
for {
|
||||||
parent, err = parent.Dir()
|
parent, err = parent.Dir()
|
||||||
@ -117,7 +118,7 @@ func computeParentPermissions(
|
|||||||
return metadata.Metadata{}, nil
|
return metadata.Metadata{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
meta, ok = folderMetas[parent.String()]
|
meta, ok = parentMetas[parent.String()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return metadata.Metadata{}, clues.New("no parent meta")
|
return metadata.Metadata{}, clues.New("no parent meta")
|
||||||
}
|
}
|
||||||
@ -137,7 +138,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.
|
||||||
@ -151,7 +152,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)
|
||||||
}
|
}
|
||||||
@ -212,7 +213,7 @@ func UpdatePermissions(
|
|||||||
return graph.Wrap(ctx, err, "setting permissions")
|
return graph.Wrap(ctx, err, "setting permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionIDMappings[p.ID] = ptr.Val(np.GetValue()[0].GetId())
|
oldPermIDToNewID[p.ID] = ptr.Val(np.GetValue()[0].GetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -228,23 +229,24 @@ 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,
|
parentMetas 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)
|
parentPermissions, err := computeParentPermissions(originDir, parentMetas)
|
||||||
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)
|
permAdded, permRemoved := metadata.DiffPermissions(parentPermissions.Permissions, meta.Permissions)
|
||||||
|
|
||||||
return UpdatePermissions(ctx, creds, service, driveID, itemID, permAdded, permRemoved, permissionIDMappings)
|
return UpdatePermissions(ctx, creds, service, driveID, itemID, permAdded, permRemoved, oldPermIDToNewID)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,10 +22,14 @@ 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")
|
runComputeParentPermissionsTest(suite, path.OneDriveService, path.FilesCategory, "user")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions_sharePoint() {
|
||||||
|
runComputeParentPermissionsTest(suite, path.SharePointService, path.LibrariesCategory, "site")
|
||||||
|
}
|
||||||
|
|
||||||
func runComputeParentPermissionsTest(
|
func runComputeParentPermissionsTest(
|
||||||
suite *PermissionsUnitTestSuite,
|
suite *PermissionsUnitTestSuite,
|
||||||
service path.ServiceType,
|
service path.ServiceType,
|
||||||
|
|||||||
@ -46,15 +46,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 +91,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 +132,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,
|
||||||
@ -157,29 +159,29 @@ func RestoreCollection(
|
|||||||
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())
|
||||||
@ -189,7 +191,7 @@ func RestoreCollection(
|
|||||||
ctx,
|
ctx,
|
||||||
drivePath,
|
drivePath,
|
||||||
dc,
|
dc,
|
||||||
folderMetas,
|
parentDirToMeta,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
restorePerms)
|
restorePerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -197,24 +199,24 @@ func RestoreCollection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
parentDirToMeta[dc.FullPath().String()] = colMeta
|
||||||
items := dc.Items(ctx, errs)
|
items := dc.Items(ctx, errs)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -247,8 +249,8 @@ func RestoreCollection(
|
|||||||
drivePath,
|
drivePath,
|
||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
folderMetas,
|
parentDirToMeta,
|
||||||
permissionIDMappings,
|
oldPermIDToNewID,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
itemData,
|
itemData,
|
||||||
itemPath)
|
itemPath)
|
||||||
@ -298,8 +300,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 +350,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 +368,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 +391,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 +441,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 +483,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 +502,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 +555,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 +564,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 +609,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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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{}{}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user