Keepers 8ee8d265b9
add clues & fault to onedrive collections (#2512)
## Does this PR need a docs update or release note?

- [ ]  No 

## Type of change

- [x] 🧹 Tech Debt/Cleanup

## Issue(s)

* #1970

## Test Plan

- [x]  Unit test
- [x] 💚 E2E
2023-02-24 22:18:53 +00:00

226 lines
5.7 KiB
Go

package onedrive
import (
"context"
"github.com/alcionai/clues"
msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/pkg/errors"
"github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/path"
)
func getParentPermissions(
parentPath path.Path,
parentPermissions map[string][]UserPermission,
) ([]UserPermission, error) {
parentPerms, ok := parentPermissions[parentPath.String()]
if !ok {
onedrivePath, err := path.ToOneDrivePath(parentPath)
if err != nil {
return nil, errors.Wrap(err, "invalid restore path")
}
if len(onedrivePath.Folders) != 0 {
return nil, errors.Wrap(err, "computing item permissions")
}
parentPerms = []UserPermission{}
}
return parentPerms, nil
}
func getParentAndCollectionPermissions(
ctx context.Context,
drivePath *path.DrivePath,
dc data.RestoreCollection,
permissions map[string][]UserPermission,
backupVersion int,
restorePerms bool,
) ([]UserPermission, []UserPermission, error) {
if !restorePerms || backupVersion < version.OneDrive1DataAndMetaFiles {
return nil, nil, nil
}
var (
err error
parentPerms []UserPermission
colPerms []UserPermission
collectionPath = dc.FullPath()
)
// Only get parent permissions if we're not restoring the root.
if len(drivePath.Folders) > 0 {
parentPath, err := collectionPath.Dir()
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 {
colPerms, err = getParentPermissions(collectionPath, permissions)
if err != nil {
return nil, nil, clues.Wrap(err, "getting collection permissions")
}
} 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 parentPerms, colPerms, nil
}
// createRestoreFoldersWithPermissions 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).
func createRestoreFoldersWithPermissions(
ctx context.Context,
service graph.Servicer,
driveID string,
restoreFolders []string,
parentPermissions []UserPermission,
folderPermissions []UserPermission,
permissionIDMappings map[string]string,
) (string, error) {
id, err := CreateRestoreFolders(ctx, service, driveID, restoreFolders)
if err != nil {
return "", err
}
err = restorePermissions(
ctx,
service,
driveID,
id,
parentPermissions,
folderPermissions,
permissionIDMappings)
return id, err
}
// getChildPermissions is to filter out permissions present in the
// parent from the ones that are available for child. This is
// necessary as we store the nested permissions in the child. We
// 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) {
var (
addedPermissions = []UserPermission{}
removedPermissions = []UserPermission{}
)
for _, cp := range childPermissions {
found := false
for _, pp := range parentPermissions {
if cp.ID == pp.ID {
found = true
break
}
}
if !found {
addedPermissions = append(addedPermissions, cp)
}
}
for _, pp := range parentPermissions {
found := false
for _, cp := range childPermissions {
if pp.ID == cp.ID {
found = true
break
}
}
if !found {
removedPermissions = append(removedPermissions, pp)
}
}
return addedPermissions, removedPermissions
}
// restorePermissions takes in the permissions that were added and the
// removed(ones present in parent but not in child) and adds/removes
// the necessary permissions on onedrive objects.
func restorePermissions(
ctx context.Context,
service graph.Servicer,
driveID string,
itemID string,
parentPerms []UserPermission,
childPerms []UserPermission,
permissionIDMappings map[string]string,
) error {
permAdded, permRemoved := getChildPermissions(childPerms, parentPerms)
ctx = clues.Add(ctx, "permission_item_id", itemID)
for _, p := range permRemoved {
err := service.Client().
DrivesById(driveID).
ItemsById(itemID).
PermissionsById(permissionIDMappings[p.ID]).
Delete(ctx, nil)
if err != nil {
return clues.Wrap(err, "removing permissions").WithClues(ctx).With(graph.ErrData(err)...)
}
}
for _, p := range permAdded {
pbody := msdrive.NewItemsItemInvitePostRequestBody()
pbody.SetRoles(p.Roles)
if p.Expiration != nil {
expiry := p.Expiration.String()
pbody.SetExpirationDateTime(&expiry)
}
si := false
pbody.SetSendInvitation(&si)
rs := true
pbody.SetRequireSignIn(&rs)
rec := models.NewDriveRecipient()
rec.SetEmail(&p.Email)
pbody.SetRecipients([]models.DriveRecipientable{rec})
np, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil)
if err != nil {
return clues.Wrap(err, "setting permissions").WithClues(ctx).With(graph.ErrData(err)...)
}
permissionIDMappings[p.ID] = *np.GetValue()[0].GetId()
}
return nil
}