Improve handling of permissions using sharing field (#2705)
Permissions are only stored if SharingMode is custom( "sharing" field is present). This will help with delta incrementals as well. --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [x] 🕐 Yes, but in a later PR - [ ] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Test - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * closes https://github.com/alcionai/corso/issues/2459 #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [ ] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
f28da24153
commit
6fd29097e3
@ -25,8 +25,12 @@ import (
|
|||||||
const versionPermissionSwitchedToID = version.OneDrive4DirIncludesPermissions
|
const versionPermissionSwitchedToID = version.OneDrive4DirIncludesPermissions
|
||||||
|
|
||||||
func getMetadata(fileName string, perm permData, permUseID bool) onedrive.Metadata {
|
func getMetadata(fileName string, perm permData, permUseID bool) onedrive.Metadata {
|
||||||
if len(perm.user) == 0 || len(perm.roles) == 0 {
|
if len(perm.user) == 0 || len(perm.roles) == 0 ||
|
||||||
return onedrive.Metadata{FileName: fileName}
|
perm.sharingMode != onedrive.SharingModeCustom {
|
||||||
|
return onedrive.Metadata{
|
||||||
|
FileName: fileName,
|
||||||
|
SharingMode: perm.sharingMode,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id := base64.StdEncoding.EncodeToString([]byte(perm.user + strings.Join(perm.roles, "+")))
|
id := base64.StdEncoding.EncodeToString([]byte(perm.user + strings.Join(perm.roles, "+")))
|
||||||
@ -262,9 +266,10 @@ func (c *onedriveCollection) withPermissions(perm permData) *onedriveCollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
type permData struct {
|
type permData struct {
|
||||||
user string // user is only for older versions
|
user string // user is only for older versions
|
||||||
entityID string
|
entityID string
|
||||||
roles []string
|
roles []string
|
||||||
|
sharingMode onedrive.SharingMode
|
||||||
}
|
}
|
||||||
|
|
||||||
type itemData struct {
|
type itemData struct {
|
||||||
@ -877,3 +882,159 @@ func (suite *GraphConnectorOneDriveIntegrationSuite) TestPermissionsRestoreAndNo
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is similar to TestPermissionsRestoreAndBackup but tests purely
|
||||||
|
// for inheritance and that too only with newer versions
|
||||||
|
func (suite *GraphConnectorOneDriveIntegrationSuite) TestPermissionsInheritanceRestoreAndBackup() {
|
||||||
|
ctx, flush := tester.NewContext()
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
// Get the default drive ID for the test user.
|
||||||
|
driveID := mustGetDefaultDriveID(
|
||||||
|
suite.T(),
|
||||||
|
ctx,
|
||||||
|
suite.connector.Service,
|
||||||
|
suite.user,
|
||||||
|
)
|
||||||
|
|
||||||
|
folderAName := "custom"
|
||||||
|
folderBName := "inherited"
|
||||||
|
|
||||||
|
rootPath := []string{
|
||||||
|
"drives",
|
||||||
|
driveID,
|
||||||
|
"root:",
|
||||||
|
}
|
||||||
|
folderAPath := []string{
|
||||||
|
"drives",
|
||||||
|
driveID,
|
||||||
|
"root:",
|
||||||
|
folderAName,
|
||||||
|
}
|
||||||
|
subfolderAPath := []string{
|
||||||
|
"drives",
|
||||||
|
driveID,
|
||||||
|
"root:",
|
||||||
|
folderAName,
|
||||||
|
folderAName,
|
||||||
|
}
|
||||||
|
subfolderBPath := []string{
|
||||||
|
"drives",
|
||||||
|
driveID,
|
||||||
|
"root:",
|
||||||
|
folderAName,
|
||||||
|
folderBName,
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSet := []itemData{
|
||||||
|
{
|
||||||
|
name: "file-custom",
|
||||||
|
data: fileAData,
|
||||||
|
perms: permData{
|
||||||
|
user: suite.secondaryUser,
|
||||||
|
entityID: suite.secondaryUserID,
|
||||||
|
roles: writePerm,
|
||||||
|
sharingMode: onedrive.SharingModeCustom,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file-inherited",
|
||||||
|
data: fileAData,
|
||||||
|
perms: permData{
|
||||||
|
sharingMode: onedrive.SharingModeInherited,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here is what this test is testing
|
||||||
|
// - custom-permission-folder
|
||||||
|
// - custom-permission-file
|
||||||
|
// - inherted-permission-file
|
||||||
|
// - custom-permission-folder
|
||||||
|
// - custom-permission-file
|
||||||
|
// - inherted-permission-file
|
||||||
|
// - inherted-permission-folder
|
||||||
|
// - custom-permission-file
|
||||||
|
// - inherted-permission-file
|
||||||
|
|
||||||
|
// No reason why it couldn't work with previous versions, but this
|
||||||
|
// is when it got introduced.
|
||||||
|
startVersion := version.OneDrive4DirIncludesPermissions
|
||||||
|
|
||||||
|
test := onedriveTest{
|
||||||
|
startVersion: startVersion,
|
||||||
|
cols: []onedriveColInfo{
|
||||||
|
{
|
||||||
|
pathElements: rootPath,
|
||||||
|
files: []itemData{},
|
||||||
|
folders: []itemData{
|
||||||
|
{
|
||||||
|
name: folderAName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pathElements: folderAPath,
|
||||||
|
files: fileSet,
|
||||||
|
folders: []itemData{
|
||||||
|
{name: folderAName},
|
||||||
|
{name: folderBName},
|
||||||
|
},
|
||||||
|
perms: permData{
|
||||||
|
user: suite.secondaryUser,
|
||||||
|
entityID: suite.secondaryUserID,
|
||||||
|
roles: readPerm,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pathElements: subfolderAPath,
|
||||||
|
files: fileSet,
|
||||||
|
perms: permData{
|
||||||
|
user: suite.secondaryUser,
|
||||||
|
entityID: suite.secondaryUserID,
|
||||||
|
roles: writePerm,
|
||||||
|
sharingMode: onedrive.SharingModeCustom,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pathElements: subfolderBPath,
|
||||||
|
files: fileSet,
|
||||||
|
perms: permData{
|
||||||
|
sharingMode: onedrive.SharingModeInherited,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := testDataForInfo(suite.T(), test.cols, version.Backup)
|
||||||
|
|
||||||
|
for vn := test.startVersion; vn <= version.Backup; vn++ {
|
||||||
|
suite.Run(fmt.Sprintf("Version%d", vn), func() {
|
||||||
|
t := suite.T()
|
||||||
|
// Ideally this can always be true or false and still
|
||||||
|
// work, but limiting older versions to use emails so as
|
||||||
|
// to validate that flow as well.
|
||||||
|
input := testDataForInfo(t, test.cols, vn)
|
||||||
|
|
||||||
|
testData := restoreBackupInfoMultiVersion{
|
||||||
|
service: path.OneDriveService,
|
||||||
|
resource: Users,
|
||||||
|
backupVersion: vn,
|
||||||
|
collectionsPrevious: input,
|
||||||
|
collectionsLatest: expected,
|
||||||
|
}
|
||||||
|
|
||||||
|
runRestoreBackupTestVersions(
|
||||||
|
t,
|
||||||
|
suite.acct,
|
||||||
|
testData,
|
||||||
|
suite.connector.tenant,
|
||||||
|
[]string{suite.user},
|
||||||
|
control.Options{
|
||||||
|
RestorePermissions: true,
|
||||||
|
ToggleFeatures: control.Toggles{EnablePermissionsBackup: true},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -3,8 +3,10 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
|
abstractions "github.com/microsoft/kiota-abstractions-go"
|
||||||
msdrives "github.com/microsoftgraph/msgraph-sdk-go/drives"
|
msdrives "github.com/microsoftgraph/msgraph-sdk-go/drives"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
mssites "github.com/microsoftgraph/msgraph-sdk-go/sites"
|
mssites "github.com/microsoftgraph/msgraph-sdk-go/sites"
|
||||||
@ -40,7 +42,18 @@ func NewItemPager(
|
|||||||
fields []string,
|
fields []string,
|
||||||
) *driveItemPager {
|
) *driveItemPager {
|
||||||
pageCount := pageSize
|
pageCount := pageSize
|
||||||
|
|
||||||
|
headers := abstractions.NewRequestHeaders()
|
||||||
|
preferHeaderItems := []string{
|
||||||
|
"deltashowremovedasdeleted",
|
||||||
|
"deltatraversepermissiongaps",
|
||||||
|
"deltashowsharingchanges",
|
||||||
|
"hierarchicalsharing",
|
||||||
|
}
|
||||||
|
headers.Add("Prefer", strings.Join(preferHeaderItems, ","))
|
||||||
|
|
||||||
requestConfig := &msdrives.ItemRootDeltaRequestBuilderGetRequestConfiguration{
|
requestConfig := &msdrives.ItemRootDeltaRequestBuilderGetRequestConfiguration{
|
||||||
|
Headers: headers,
|
||||||
QueryParameters: &msdrives.ItemRootDeltaRequestBuilderGetQueryParameters{
|
QueryParameters: &msdrives.ItemRootDeltaRequestBuilderGetQueryParameters{
|
||||||
Top: &pageCount,
|
Top: &pageCount,
|
||||||
Select: fields,
|
Select: fields,
|
||||||
|
|||||||
@ -53,6 +53,13 @@ var (
|
|||||||
_ data.StreamModTime = &metadataItem{}
|
_ data.StreamModTime = &metadataItem{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SharingMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SharingModeCustom = SharingMode(iota)
|
||||||
|
SharingModeInherited
|
||||||
|
)
|
||||||
|
|
||||||
// Collection represents a set of OneDrive objects retrieved from M365
|
// Collection represents a set of OneDrive objects retrieved from M365
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
// configured to handle large item downloads
|
// configured to handle large item downloads
|
||||||
@ -213,7 +220,11 @@ type UserPermission struct {
|
|||||||
// 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 {
|
||||||
FileName string `json:"filename,omitempty"`
|
FileName string `json:"filename,omitempty"`
|
||||||
|
// SharingMode denotes what the current mode of sharing is for the object.
|
||||||
|
// - inherited: permissions same as parent permissions (no "shared" in delta)
|
||||||
|
// - custom: use Permissions to set correct permissions ("shared" has value in delta)
|
||||||
|
SharingMode SharingMode `json:"permissionMode,omitempty"`
|
||||||
Permissions []UserPermission `json:"permissions,omitempty"`
|
Permissions []UserPermission `json:"permissions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -622,6 +622,14 @@ func (c *Collections) UpdateCollections(
|
|||||||
isFolder = item.GetFolder() != nil || item.GetPackage() != nil
|
isFolder = item.GetFolder() != nil || item.GetPackage() != nil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(meain): Use `@microsoft.graph.sharedChanged` in
|
||||||
|
// item.GetAdditionalData() to optimize fetching
|
||||||
|
// permissions. When permissions change, this flags is
|
||||||
|
// present, if only data changes, it is not present. Have to
|
||||||
|
// add `oneDrive.sharedChanged` in `$select` in delta
|
||||||
|
// https://learn.microsoft.com/en-us/onedrive/developer/rest-api
|
||||||
|
// /concepts/scan-guidance#scanning-permissions-hierarchies
|
||||||
|
|
||||||
// Deleted file or folder.
|
// Deleted file or folder.
|
||||||
if item.GetDeleted() != nil {
|
if item.GetDeleted() != nil {
|
||||||
if err := c.handleDelete(
|
if err := c.handleDelete(
|
||||||
|
|||||||
@ -173,6 +173,7 @@ func defaultItemPager(
|
|||||||
"size",
|
"size",
|
||||||
"deleted",
|
"deleted",
|
||||||
"malware",
|
"malware",
|
||||||
|
"shared",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,14 +73,27 @@ func oneDriveItemMetaReader(
|
|||||||
FileName: *item.GetName(),
|
FileName: *item.GetName(),
|
||||||
}
|
}
|
||||||
|
|
||||||
perms, err := oneDriveItemPermissionInfo(ctx, service, driveID, item, fetchPermissions)
|
if item.GetShared() == nil {
|
||||||
if err != nil {
|
meta.SharingMode = SharingModeInherited
|
||||||
// Keep this in an if-block because if it's not then we have a weird issue
|
|
||||||
// of having no value in error but golang thinking it's non nil because of
|
|
||||||
// the way interfaces work.
|
|
||||||
err = clues.Wrap(err, "fetching item permissions")
|
|
||||||
} else {
|
} else {
|
||||||
meta.Permissions = perms
|
meta.SharingMode = SharingModeCustom
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
perms []UserPermission
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if meta.SharingMode == SharingModeCustom && fetchPermissions {
|
||||||
|
perms, err = oneDriveItemPermissionInfo(ctx, service, driveID, ptr.Val(item.GetId()))
|
||||||
|
if err != nil {
|
||||||
|
// Keep this in an if-block because if it's not then we have a weird issue
|
||||||
|
// of having no value in error but golang thinking it's non nil because of
|
||||||
|
// the way interfaces work.
|
||||||
|
err = clues.Wrap(err, "fetching item permissions")
|
||||||
|
} else {
|
||||||
|
meta.Permissions = perms
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metaJSON, serializeErr := json.Marshal(meta)
|
metaJSON, serializeErr := json.Marshal(meta)
|
||||||
@ -219,29 +232,22 @@ func oneDriveItemInfo(di models.DriveItemable, itemSize int64) *details.OneDrive
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// oneDriveItemPermissionInfo will fetch the permission information for a drive
|
// OneDriveItemPermissionInfo will fetch the permission information
|
||||||
// item.
|
// for a drive item given a drive and item id.
|
||||||
func oneDriveItemPermissionInfo(
|
func oneDriveItemPermissionInfo(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
driveID string,
|
driveID string,
|
||||||
di models.DriveItemable,
|
itemID string,
|
||||||
fetchPermissions bool,
|
|
||||||
) ([]UserPermission, error) {
|
) ([]UserPermission, error) {
|
||||||
if !fetchPermissions {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
id := ptr.Val(di.GetId())
|
|
||||||
|
|
||||||
perm, err := service.
|
perm, err := service.
|
||||||
Client().
|
Client().
|
||||||
DrivesById(driveID).
|
DrivesById(driveID).
|
||||||
ItemsById(id).
|
ItemsById(itemID).
|
||||||
Permissions().
|
Permissions().
|
||||||
Get(ctx, nil)
|
Get(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, graph.Wrap(ctx, err, "getting item metadata").With("item_id", id)
|
return nil, graph.Wrap(ctx, err, "getting item metadata").With("item_id", itemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
uperms := filterUserPermissions(ctx, perm.GetValue())
|
uperms := filterUserPermissions(ctx, perm.GetValue())
|
||||||
|
|||||||
@ -14,80 +14,70 @@ import (
|
|||||||
"github.com/alcionai/corso/src/pkg/path"
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getParentPermissions(
|
func getParentMetadata(
|
||||||
parentPath path.Path,
|
parentPath path.Path,
|
||||||
parentPermissions map[string][]UserPermission,
|
metas map[string]Metadata,
|
||||||
) ([]UserPermission, error) {
|
) (Metadata, error) {
|
||||||
parentPerms, ok := parentPermissions[parentPath.String()]
|
parentMeta, ok := metas[parentPath.String()]
|
||||||
if !ok {
|
if !ok {
|
||||||
onedrivePath, err := path.ToOneDrivePath(parentPath)
|
onedrivePath, err := path.ToOneDrivePath(parentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "invalid restore path")
|
return Metadata{}, errors.Wrap(err, "invalid restore path")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(onedrivePath.Folders) != 0 {
|
if len(onedrivePath.Folders) != 0 {
|
||||||
return nil, errors.Wrap(err, "computing item permissions")
|
return Metadata{}, errors.Wrap(err, "computing item permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
parentPerms = []UserPermission{}
|
parentMeta = Metadata{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parentPerms, nil
|
return parentMeta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getParentAndCollectionPermissions(
|
func getCollectionMetadata(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
drivePath *path.DrivePath,
|
drivePath *path.DrivePath,
|
||||||
dc data.RestoreCollection,
|
dc data.RestoreCollection,
|
||||||
permissions map[string][]UserPermission,
|
metas map[string]Metadata,
|
||||||
backupVersion int,
|
backupVersion int,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
) ([]UserPermission, []UserPermission, error) {
|
) (Metadata, error) {
|
||||||
if !restorePerms || backupVersion < version.OneDrive1DataAndMetaFiles {
|
if !restorePerms || backupVersion < version.OneDrive1DataAndMetaFiles {
|
||||||
return nil, nil, nil
|
return Metadata{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
parentPerms []UserPermission
|
|
||||||
colPerms []UserPermission
|
|
||||||
collectionPath = dc.FullPath()
|
collectionPath = dc.FullPath()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Only get parent permissions if we're not restoring the root.
|
if len(drivePath.Folders) == 0 {
|
||||||
if len(drivePath.Folders) > 0 {
|
// No permissions for root folder
|
||||||
parentPath, err := collectionPath.Dir()
|
return Metadata{}, nil
|
||||||
if err != nil {
|
|
||||||
return nil, nil, clues.Wrap(err, "getting parent path")
|
|
||||||
}
|
|
||||||
|
|
||||||
parentPerms, err = getParentPermissions(parentPath, permissions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, clues.Wrap(err, "getting parent permissions")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if backupVersion < version.OneDrive4DirIncludesPermissions {
|
if backupVersion < version.OneDrive4DirIncludesPermissions {
|
||||||
colPerms, err = getParentPermissions(collectionPath, permissions)
|
colMeta, err := getParentMetadata(collectionPath, metas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, clues.Wrap(err, "getting collection permissions")
|
return Metadata{}, clues.Wrap(err, "collection metadata")
|
||||||
}
|
|
||||||
} else if len(drivePath.Folders) > 0 {
|
|
||||||
// Root folder doesn't have a metadata file associated with it.
|
|
||||||
folders := collectionPath.Folders()
|
|
||||||
|
|
||||||
meta, err := fetchAndReadMetadata(
|
|
||||||
ctx,
|
|
||||||
dc,
|
|
||||||
folders[len(folders)-1]+DirMetaFileSuffix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, clues.Wrap(err, "collection permissions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
colPerms = meta.Permissions
|
return colMeta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return parentPerms, colPerms, nil
|
// Root folder doesn't have a metadata file associated with it.
|
||||||
|
folders := collectionPath.Folders()
|
||||||
|
|
||||||
|
meta, err := fetchAndReadMetadata(
|
||||||
|
ctx,
|
||||||
|
dc,
|
||||||
|
folders[len(folders)-1]+DirMetaFileSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return Metadata{}, clues.Wrap(err, "collection metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRestoreFoldersWithPermissions creates the restore folder hierarchy in
|
// createRestoreFoldersWithPermissions creates the restore folder hierarchy in
|
||||||
@ -99,8 +89,7 @@ func createRestoreFoldersWithPermissions(
|
|||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
driveID string,
|
driveID string,
|
||||||
restoreFolders []string,
|
restoreFolders []string,
|
||||||
parentPermissions []UserPermission,
|
folderMetadata Metadata,
|
||||||
folderPermissions []UserPermission,
|
|
||||||
permissionIDMappings map[string]string,
|
permissionIDMappings map[string]string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
id, err := CreateRestoreFolders(ctx, service, driveID, restoreFolders)
|
id, err := CreateRestoreFolders(ctx, service, driveID, restoreFolders)
|
||||||
@ -113,58 +102,52 @@ func createRestoreFoldersWithPermissions(
|
|||||||
service,
|
service,
|
||||||
driveID,
|
driveID,
|
||||||
id,
|
id,
|
||||||
parentPermissions,
|
folderMetadata,
|
||||||
folderPermissions,
|
|
||||||
permissionIDMappings)
|
permissionIDMappings)
|
||||||
|
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getChildPermissions is to filter out permissions present in the
|
func diffPermissions(
|
||||||
// parent from the ones that are available for child. This is
|
before, after []UserPermission,
|
||||||
// necessary as we store the nested permissions in the child. We
|
permissionIDMappings map[string]string,
|
||||||
// cannot avoid storing the nested permissions as it is possible that
|
|
||||||
// a file in a folder can remove the nested permission that is present
|
|
||||||
// on itself.
|
|
||||||
func getChildPermissions(
|
|
||||||
childPermissions, parentPermissions []UserPermission,
|
|
||||||
) ([]UserPermission, []UserPermission) {
|
) ([]UserPermission, []UserPermission) {
|
||||||
var (
|
var (
|
||||||
addedPermissions = []UserPermission{}
|
added = []UserPermission{}
|
||||||
removedPermissions = []UserPermission{}
|
removed = []UserPermission{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, cp := range childPermissions {
|
for _, cp := range after {
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
for _, pp := range parentPermissions {
|
for _, pp := range before {
|
||||||
if cp.ID == pp.ID {
|
if cp.ID == permissionIDMappings[pp.ID] {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
addedPermissions = append(addedPermissions, cp)
|
added = append(added, cp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pp := range parentPermissions {
|
for _, pp := range before {
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
for _, cp := range childPermissions {
|
for _, cp := range after {
|
||||||
if pp.ID == cp.ID {
|
if permissionIDMappings[pp.ID] == cp.ID {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
removedPermissions = append(removedPermissions, pp)
|
removed = append(removed, pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return addedPermissions, removedPermissions
|
return added, removed
|
||||||
}
|
}
|
||||||
|
|
||||||
// restorePermissions takes in the permissions that were added and the
|
// restorePermissions takes in the permissions that were added and the
|
||||||
@ -175,19 +158,28 @@ func restorePermissions(
|
|||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
driveID string,
|
driveID string,
|
||||||
itemID string,
|
itemID string,
|
||||||
parentPerms []UserPermission,
|
meta Metadata,
|
||||||
childPerms []UserPermission,
|
|
||||||
permissionIDMappings map[string]string,
|
permissionIDMappings map[string]string,
|
||||||
) error {
|
) error {
|
||||||
permAdded, permRemoved := getChildPermissions(childPerms, parentPerms)
|
if meta.SharingMode == SharingModeInherited {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
ctx = clues.Add(ctx, "permission_item_id", itemID)
|
ctx = clues.Add(ctx, "permission_item_id", itemID)
|
||||||
|
|
||||||
|
// TODO(meain): Compute this from the data that we have instead of fetching from graph
|
||||||
|
currentPermissions, err := oneDriveItemPermissionInfo(ctx, service, driveID, itemID)
|
||||||
|
if err != nil {
|
||||||
|
return graph.Wrap(ctx, err, "fetching current permissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
permAdded, permRemoved := diffPermissions(currentPermissions, meta.Permissions, permissionIDMappings)
|
||||||
|
|
||||||
for _, p := range permRemoved {
|
for _, p := range permRemoved {
|
||||||
err := service.Client().
|
err := service.Client().
|
||||||
DrivesById(driveID).
|
DrivesById(driveID).
|
||||||
ItemsById(itemID).
|
ItemsById(itemID).
|
||||||
PermissionsById(permissionIDMappings[p.ID]).
|
PermissionsById(p.ID).
|
||||||
Delete(ctx, nil)
|
Delete(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graph.Wrap(ctx, err, "removing permissions")
|
return graph.Wrap(ctx, err, "removing permissions")
|
||||||
|
|||||||
@ -44,7 +44,7 @@ func RestoreCollections(
|
|||||||
var (
|
var (
|
||||||
restoreMetrics support.CollectionMetrics
|
restoreMetrics support.CollectionMetrics
|
||||||
metrics support.CollectionMetrics
|
metrics support.CollectionMetrics
|
||||||
folderPerms map[string][]UserPermission
|
folderMetas map[string]Metadata
|
||||||
|
|
||||||
// permissionIDMappings is used to map between old and new id
|
// permissionIDMappings is used to map between old and new id
|
||||||
// of permissions as we restore them
|
// of permissions as we restore them
|
||||||
@ -63,8 +63,8 @@ func RestoreCollections(
|
|||||||
})
|
})
|
||||||
|
|
||||||
var (
|
var (
|
||||||
el = errs.Local()
|
el = errs.Local()
|
||||||
parentPermissions = map[string][]UserPermission{}
|
parentMetas = map[string]Metadata{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Iterate through the data collections and restore the contents of each
|
// Iterate through the data collections and restore the contents of each
|
||||||
@ -82,12 +82,12 @@ func RestoreCollections(
|
|||||||
"path", dc.FullPath()) // TODO: pii
|
"path", dc.FullPath()) // TODO: pii
|
||||||
)
|
)
|
||||||
|
|
||||||
metrics, folderPerms, permissionIDMappings, err = RestoreCollection(
|
metrics, folderMetas, permissionIDMappings, err = RestoreCollection(
|
||||||
ictx,
|
ictx,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
service,
|
service,
|
||||||
dc,
|
dc,
|
||||||
parentPermissions,
|
parentMetas,
|
||||||
OneDriveSource,
|
OneDriveSource,
|
||||||
dest.ContainerName,
|
dest.ContainerName,
|
||||||
deets,
|
deets,
|
||||||
@ -98,8 +98,8 @@ func RestoreCollections(
|
|||||||
el.AddRecoverable(err)
|
el.AddRecoverable(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range folderPerms {
|
for k, v := range folderMetas {
|
||||||
parentPermissions[k] = v
|
parentMetas[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreMetrics = support.CombineMetrics(restoreMetrics, metrics)
|
restoreMetrics = support.CombineMetrics(restoreMetrics, metrics)
|
||||||
@ -128,20 +128,20 @@ func RestoreCollection(
|
|||||||
backupVersion int,
|
backupVersion int,
|
||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
dc data.RestoreCollection,
|
dc data.RestoreCollection,
|
||||||
parentPermissions map[string][]UserPermission,
|
parentMetas map[string]Metadata,
|
||||||
source driveSource,
|
source driveSource,
|
||||||
restoreContainerName string,
|
restoreContainerName string,
|
||||||
deets *details.Builder,
|
deets *details.Builder,
|
||||||
permissionIDMappings map[string]string,
|
permissionIDMappings map[string]string,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
errs *fault.Bus,
|
errs *fault.Bus,
|
||||||
) (support.CollectionMetrics, map[string][]UserPermission, map[string]string, error) {
|
) (support.CollectionMetrics, map[string]Metadata, map[string]string, error) {
|
||||||
var (
|
var (
|
||||||
metrics = support.CollectionMetrics{}
|
metrics = support.CollectionMetrics{}
|
||||||
copyBuffer = make([]byte, copyBufferSize)
|
copyBuffer = make([]byte, copyBufferSize)
|
||||||
directory = dc.FullPath()
|
directory = dc.FullPath()
|
||||||
itemInfo details.ItemInfo
|
itemInfo details.ItemInfo
|
||||||
folderPerms = map[string][]UserPermission{}
|
folderMetas = map[string]Metadata{}
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, end := D.Span(ctx, "gc:oneDrive:restoreCollection", D.Label("path", directory))
|
ctx, end := D.Span(ctx, "gc:oneDrive:restoreCollection", D.Label("path", directory))
|
||||||
@ -149,7 +149,7 @@ func RestoreCollection(
|
|||||||
|
|
||||||
drivePath, err := path.ToOneDrivePath(directory)
|
drivePath, err := path.ToOneDrivePath(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderPerms, permissionIDMappings, clues.Wrap(err, "creating drive path").WithClues(ctx)
|
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "creating drive path").WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble folder hierarchy we're going to restore into (we recreate the folder hierarchy
|
// Assemble folder hierarchy we're going to restore into (we recreate the folder hierarchy
|
||||||
@ -168,15 +168,15 @@ func RestoreCollection(
|
|||||||
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")
|
||||||
|
|
||||||
parentPerms, colPerms, err := getParentAndCollectionPermissions(
|
colMeta, err := getCollectionMetadata(
|
||||||
ctx,
|
ctx,
|
||||||
drivePath,
|
drivePath,
|
||||||
dc,
|
dc,
|
||||||
parentPermissions,
|
parentMetas,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
restorePerms)
|
restorePerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderPerms, permissionIDMappings, clues.Wrap(err, "getting permissions").WithClues(ctx)
|
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "getting permissions").WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create restore folders and get the folder ID of the folder the data stream will be restored in
|
// Create restore folders and get the folder ID of the folder the data stream will be restored in
|
||||||
@ -185,12 +185,11 @@ func RestoreCollection(
|
|||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
restoreFolderElements,
|
restoreFolderElements,
|
||||||
parentPerms,
|
colMeta,
|
||||||
colPerms,
|
|
||||||
permissionIDMappings,
|
permissionIDMappings,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderPerms, permissionIDMappings, clues.Wrap(err, "creating folders for restore")
|
return metrics, folderMetas, permissionIDMappings, clues.Wrap(err, "creating folders for restore")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -205,11 +204,11 @@ func RestoreCollection(
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return metrics, folderPerms, permissionIDMappings, err
|
return metrics, folderMetas, permissionIDMappings, err
|
||||||
|
|
||||||
case itemData, ok := <-items:
|
case itemData, ok := <-items:
|
||||||
if !ok {
|
if !ok {
|
||||||
return metrics, folderPerms, permissionIDMappings, nil
|
return metrics, folderMetas, permissionIDMappings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
itemPath, err := dc.FullPath().Append(itemData.UUID(), true)
|
itemPath, err := dc.FullPath().Append(itemData.UUID(), true)
|
||||||
@ -239,7 +238,6 @@ func RestoreCollection(
|
|||||||
dc,
|
dc,
|
||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
colPerms,
|
|
||||||
permissionIDMappings,
|
permissionIDMappings,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
itemData,
|
itemData,
|
||||||
@ -253,7 +251,6 @@ func RestoreCollection(
|
|||||||
dc,
|
dc,
|
||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
colPerms,
|
|
||||||
permissionIDMappings,
|
permissionIDMappings,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
itemData,
|
itemData,
|
||||||
@ -296,7 +293,7 @@ func RestoreCollection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
trimmedPath := strings.TrimSuffix(itemPath.String(), DirMetaFileSuffix)
|
trimmedPath := strings.TrimSuffix(itemPath.String(), DirMetaFileSuffix)
|
||||||
folderPerms[trimmedPath] = meta.Permissions
|
folderMetas[trimmedPath] = meta
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -330,7 +327,7 @@ func RestoreCollection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return metrics, folderPerms, permissionIDMappings, el.Failure()
|
return metrics, folderMetas, permissionIDMappings, el.Failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileFetcher interface {
|
type fileFetcher interface {
|
||||||
@ -345,7 +342,6 @@ func restoreV1File(
|
|||||||
fetcher fileFetcher,
|
fetcher fileFetcher,
|
||||||
restoreFolderID string,
|
restoreFolderID string,
|
||||||
copyBuffer []byte,
|
copyBuffer []byte,
|
||||||
parentPerms []UserPermission,
|
|
||||||
permissionIDMappings map[string]string,
|
permissionIDMappings map[string]string,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
itemData data.Stream,
|
itemData data.Stream,
|
||||||
@ -384,8 +380,7 @@ func restoreV1File(
|
|||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
itemID,
|
itemID,
|
||||||
parentPerms,
|
meta,
|
||||||
meta.Permissions,
|
|
||||||
permissionIDMappings,
|
permissionIDMappings,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -403,7 +398,6 @@ func restoreV2File(
|
|||||||
fetcher fileFetcher,
|
fetcher fileFetcher,
|
||||||
restoreFolderID string,
|
restoreFolderID string,
|
||||||
copyBuffer []byte,
|
copyBuffer []byte,
|
||||||
parentPerms []UserPermission,
|
|
||||||
permissionIDMappings map[string]string,
|
permissionIDMappings map[string]string,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
itemData data.Stream,
|
itemData data.Stream,
|
||||||
@ -453,8 +447,7 @@ func restoreV2File(
|
|||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
itemID,
|
itemID,
|
||||||
parentPerms,
|
meta,
|
||||||
meta.Permissions,
|
|
||||||
permissionIDMappings,
|
permissionIDMappings,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -71,7 +71,7 @@ func RestoreCollections(
|
|||||||
backupVersion,
|
backupVersion,
|
||||||
service,
|
service,
|
||||||
dc,
|
dc,
|
||||||
map[string][]onedrive.UserPermission{}, // Currently permission data is not stored for sharepoint
|
map[string]onedrive.Metadata{}, // Currently permission data is not stored for sharepoint
|
||||||
onedrive.SharePointSource,
|
onedrive.SharePointSource,
|
||||||
dest.ContainerName,
|
dest.ContainerName,
|
||||||
deets,
|
deets,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user