Compute parent permissions from available metadata (#3022)
Previously we were querying Graph to get the current permissions and then using that to compute the permission diff. This changes that to use the metadata that we have available locally to do the computation avoiding a graph call to get permissions. <!-- PR description--> --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 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. --> * fixes https://github.com/alcionai/corso/issues/2976 #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
dd30c0e0a2
commit
b1606466c2
@ -2,13 +2,13 @@ package connector
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -37,7 +37,10 @@ func getMetadata(fileName string, perm permData, permUseID bool) onedrive.Metada
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id := base64.StdEncoding.EncodeToString([]byte(perm.user + strings.Join(perm.roles, "+")))
|
// In case of permissions, the id will usually be same for same
|
||||||
|
// user/role combo unless deleted and readded, but we have to do
|
||||||
|
// this as we only have two users of which one is already taken.
|
||||||
|
id := uuid.NewString()
|
||||||
uperm := onedrive.UserPermission{ID: id, Roles: perm.roles}
|
uperm := onedrive.UserPermission{ID: id, Roles: perm.roles}
|
||||||
|
|
||||||
if permUseID {
|
if permUseID {
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import (
|
|||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive"
|
msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
"github.com/alcionai/corso/src/internal/data"
|
"github.com/alcionai/corso/src/internal/data"
|
||||||
"github.com/alcionai/corso/src/internal/version"
|
"github.com/alcionai/corso/src/internal/version"
|
||||||
@ -93,7 +93,11 @@ func createRestoreFoldersWithPermissions(
|
|||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
drivePath *path.DrivePath,
|
drivePath *path.DrivePath,
|
||||||
restoreFolders []string,
|
restoreFolders []string,
|
||||||
|
folderPath path.Path,
|
||||||
folderMetadata Metadata,
|
folderMetadata Metadata,
|
||||||
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
|
restorePerms bool,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
id, err := CreateRestoreFolders(ctx, service, drivePath.DriveID, restoreFolders)
|
id, err := CreateRestoreFolders(ctx, service, drivePath.DriveID, restoreFolders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -105,28 +109,25 @@ func createRestoreFoldersWithPermissions(
|
|||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !restorePerms {
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
err = RestorePermissions(
|
err = RestorePermissions(
|
||||||
ctx,
|
ctx,
|
||||||
creds,
|
creds,
|
||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
id,
|
id,
|
||||||
folderMetadata)
|
folderPath,
|
||||||
|
folderMetadata,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings)
|
||||||
|
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// isSame checks equality of two string slices
|
func diffPermissions(before, after []UserPermission) ([]UserPermission, []UserPermission) {
|
||||||
func isSame(first, second []string) bool {
|
|
||||||
slices.Sort(first)
|
|
||||||
slices.Sort(second)
|
|
||||||
|
|
||||||
return slices.Equal(first, second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func diffPermissions(
|
|
||||||
before, after []UserPermission,
|
|
||||||
) ([]UserPermission, []UserPermission) {
|
|
||||||
var (
|
var (
|
||||||
added = []UserPermission{}
|
added = []UserPermission{}
|
||||||
removed = []UserPermission{}
|
removed = []UserPermission{}
|
||||||
@ -136,8 +137,7 @@ func diffPermissions(
|
|||||||
found := false
|
found := false
|
||||||
|
|
||||||
for _, pp := range before {
|
for _, pp := range before {
|
||||||
if isSame(cp.Roles, pp.Roles) &&
|
if pp.ID == cp.ID {
|
||||||
cp.EntityID == pp.EntityID {
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -152,8 +152,7 @@ func diffPermissions(
|
|||||||
found := false
|
found := false
|
||||||
|
|
||||||
for _, cp := range after {
|
for _, cp := range after {
|
||||||
if isSame(cp.Roles, pp.Roles) &&
|
if pp.ID == cp.ID {
|
||||||
cp.EntityID == pp.EntityID {
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -167,31 +166,58 @@ func diffPermissions(
|
|||||||
return added, removed
|
return added, removed
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestorePermissions takes in the permissions that were added and the
|
// computeParentPermissions computes the parent permissions by
|
||||||
// removed(ones present in parent but not in child) and adds/removes
|
// traversing folderMetas and finding the first item with custom
|
||||||
// the necessary permissions on onedrive objects.
|
// permissions. folderMetas is expected to have all the parent
|
||||||
func RestorePermissions(
|
// directory metas for this to work.
|
||||||
|
func computeParentPermissions(itemPath path.Path, folderMetas map[string]Metadata) (Metadata, error) {
|
||||||
|
var (
|
||||||
|
parent path.Path
|
||||||
|
meta Metadata
|
||||||
|
|
||||||
|
err error
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
parent = itemPath
|
||||||
|
|
||||||
|
for {
|
||||||
|
parent, err = parent.Dir()
|
||||||
|
if err != nil {
|
||||||
|
return Metadata{}, clues.New("getting parent")
|
||||||
|
}
|
||||||
|
|
||||||
|
onedrivePath, err := path.ToOneDrivePath(parent)
|
||||||
|
if err != nil {
|
||||||
|
return Metadata{}, clues.New("get parent path")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(onedrivePath.Folders) == 0 {
|
||||||
|
return Metadata{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, ok = folderMetas[parent.String()]
|
||||||
|
if !ok {
|
||||||
|
return Metadata{}, clues.New("no parent meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
if meta.SharingMode == SharingModeCustom {
|
||||||
|
return meta, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePermissions takes in the set of permission to be added and
|
||||||
|
// removed from an item to bring it to the desired state.
|
||||||
|
func UpdatePermissions(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
creds account.M365Config,
|
creds account.M365Config,
|
||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
driveID string,
|
driveID string,
|
||||||
itemID string,
|
itemID string,
|
||||||
meta Metadata,
|
permAdded, permRemoved []UserPermission,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
) error {
|
) error {
|
||||||
if meta.SharingMode == SharingModeInherited {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := driveItemPermissionInfo(ctx, service, driveID, itemID)
|
|
||||||
if err != nil {
|
|
||||||
return graph.Wrap(ctx, err, "fetching current permissions")
|
|
||||||
}
|
|
||||||
|
|
||||||
permAdded, permRemoved := diffPermissions(currentPermissions, meta.Permissions)
|
|
||||||
|
|
||||||
for _, p := range permRemoved {
|
for _, p := range permRemoved {
|
||||||
// deletes require unique http clients
|
// deletes require unique http clients
|
||||||
// https://github.com/alcionai/corso/issues/2707
|
// https://github.com/alcionai/corso/issues/2707
|
||||||
@ -202,11 +228,16 @@ func RestorePermissions(
|
|||||||
return graph.Wrap(ctx, err, "creating delete client")
|
return graph.Wrap(ctx, err, "creating delete client")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid, ok := permissionIDMappings[p.ID]
|
||||||
|
if !ok {
|
||||||
|
return clues.New("no new permission id").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
err = graph.NewService(a).
|
err = graph.NewService(a).
|
||||||
Client().
|
Client().
|
||||||
DrivesById(driveID).
|
DrivesById(driveID).
|
||||||
ItemsById(itemID).
|
ItemsById(itemID).
|
||||||
PermissionsById(p.ID).
|
PermissionsById(pid).
|
||||||
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")
|
||||||
@ -253,11 +284,44 @@ func RestorePermissions(
|
|||||||
|
|
||||||
pbody.SetRecipients([]models.DriveRecipientable{rec})
|
pbody.SetRecipients([]models.DriveRecipientable{rec})
|
||||||
|
|
||||||
_, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil)
|
np, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graph.Wrap(ctx, err, "setting permissions")
|
return graph.Wrap(ctx, err, "setting permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
permissionIDMappings[p.ID] = ptr.Val(np.GetValue()[0].GetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestorePermissions takes in the permissions of an item, computes
|
||||||
|
// what permissions need to added and removed based on the parent
|
||||||
|
// folder metas and uses that to add/remove the necessary permissions
|
||||||
|
// on onedrive items.
|
||||||
|
func RestorePermissions(
|
||||||
|
ctx context.Context,
|
||||||
|
creds account.M365Config,
|
||||||
|
service graph.Servicer,
|
||||||
|
driveID string,
|
||||||
|
itemID string,
|
||||||
|
itemPath path.Path,
|
||||||
|
meta Metadata,
|
||||||
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
|
) error {
|
||||||
|
if meta.SharingMode == SharingModeInherited {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = clues.Add(ctx, "permission_item_id", itemID)
|
||||||
|
|
||||||
|
parentPermissions, err := computeParentPermissions(itemPath, folderMetas)
|
||||||
|
if err != nil {
|
||||||
|
return clues.Wrap(err, "parent permissions").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
permAdded, permRemoved := diffPermissions(parentPermissions.Permissions, meta.Permissions)
|
||||||
|
|
||||||
|
return UpdatePermissions(ctx, creds, service, driveID, itemID, permAdded, permRemoved, permissionIDMappings)
|
||||||
|
}
|
||||||
|
|||||||
153
src/internal/connector/onedrive/permission_test.go
Normal file
153
src/internal/connector/onedrive/permission_test.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package onedrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PermissionsUnitTestSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissionsUnitTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &PermissionsUnitTestSuite{Suite: tester.NewUnitSuite(t)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *PermissionsUnitTestSuite) TestComputeParentPermissions() {
|
||||||
|
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,
|
||||||
|
false,
|
||||||
|
strings.Split(entryPath, "/")...,
|
||||||
|
)
|
||||||
|
require.NoError(suite.T(), err, "creating path")
|
||||||
|
|
||||||
|
rootEntry, err := path.Build(
|
||||||
|
"tenant",
|
||||||
|
"user",
|
||||||
|
path.OneDriveService,
|
||||||
|
path.FilesCategory,
|
||||||
|
false,
|
||||||
|
strings.Split(rootEntryPath, "/")...,
|
||||||
|
)
|
||||||
|
require.NoError(suite.T(), err, "creating path")
|
||||||
|
|
||||||
|
level2, err := entry.Dir()
|
||||||
|
require.NoError(suite.T(), err, "level2 path")
|
||||||
|
|
||||||
|
level1, err := level2.Dir()
|
||||||
|
require.NoError(suite.T(), err, "level1 path")
|
||||||
|
|
||||||
|
level0, err := level1.Dir()
|
||||||
|
require.NoError(suite.T(), err, "level0 path")
|
||||||
|
|
||||||
|
metadata := Metadata{
|
||||||
|
SharingMode: SharingModeCustom,
|
||||||
|
Permissions: []UserPermission{
|
||||||
|
{
|
||||||
|
Roles: []string{"write"},
|
||||||
|
EntityID: "user-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata2 := Metadata{
|
||||||
|
SharingMode: SharingModeCustom,
|
||||||
|
Permissions: []UserPermission{
|
||||||
|
{
|
||||||
|
Roles: []string{"read"},
|
||||||
|
EntityID: "user-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
inherited := Metadata{
|
||||||
|
SharingMode: SharingModeInherited,
|
||||||
|
Permissions: []UserPermission{},
|
||||||
|
}
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
item path.Path
|
||||||
|
meta Metadata
|
||||||
|
parentPerms map[string]Metadata
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "root level entry",
|
||||||
|
item: rootEntry,
|
||||||
|
meta: Metadata{},
|
||||||
|
parentPerms: map[string]Metadata{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root level directory",
|
||||||
|
item: level0,
|
||||||
|
meta: Metadata{},
|
||||||
|
parentPerms: map[string]Metadata{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "direct parent perms",
|
||||||
|
item: entry,
|
||||||
|
meta: metadata,
|
||||||
|
parentPerms: map[string]Metadata{
|
||||||
|
level2.String(): metadata,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "top level parent perms",
|
||||||
|
item: entry,
|
||||||
|
meta: metadata,
|
||||||
|
parentPerms: map[string]Metadata{
|
||||||
|
level2.String(): inherited,
|
||||||
|
level1.String(): inherited,
|
||||||
|
level0.String(): metadata,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all inherited",
|
||||||
|
item: entry,
|
||||||
|
meta: Metadata{},
|
||||||
|
parentPerms: map[string]Metadata{
|
||||||
|
level2.String(): inherited,
|
||||||
|
level1.String(): inherited,
|
||||||
|
level0.String(): inherited,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple custom permission",
|
||||||
|
item: entry,
|
||||||
|
meta: metadata,
|
||||||
|
parentPerms: map[string]Metadata{
|
||||||
|
level2.String(): inherited,
|
||||||
|
level1.String(): metadata,
|
||||||
|
level0.String(): metadata2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
_, flush := tester.NewContext()
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
m, err := computeParentPermissions(test.item, test.parentPerms)
|
||||||
|
require.NoError(t, err, "compute permissions")
|
||||||
|
|
||||||
|
assert.Equal(t, m, test.meta)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,7 +46,11 @@ func RestoreCollections(
|
|||||||
var (
|
var (
|
||||||
restoreMetrics support.CollectionMetrics
|
restoreMetrics support.CollectionMetrics
|
||||||
metrics support.CollectionMetrics
|
metrics support.CollectionMetrics
|
||||||
folderMetas map[string]Metadata
|
folderMetas = map[string]Metadata{}
|
||||||
|
|
||||||
|
// permissionIDMappings is used to map between old and new id
|
||||||
|
// of permissions as we restore them
|
||||||
|
permissionIDMappings = map[string]string{}
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx = clues.Add(
|
ctx = clues.Add(
|
||||||
@ -60,10 +64,7 @@ func RestoreCollections(
|
|||||||
return dcs[i].FullPath().String() < dcs[j].FullPath().String()
|
return dcs[i].FullPath().String() < dcs[j].FullPath().String()
|
||||||
})
|
})
|
||||||
|
|
||||||
var (
|
el := errs.Local()
|
||||||
el = errs.Local()
|
|
||||||
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
|
||||||
for _, dc := range dcs {
|
for _, dc := range dcs {
|
||||||
@ -80,13 +81,14 @@ func RestoreCollections(
|
|||||||
"path", dc.FullPath())
|
"path", dc.FullPath())
|
||||||
)
|
)
|
||||||
|
|
||||||
metrics, folderMetas, err = RestoreCollection(
|
metrics, err = RestoreCollection(
|
||||||
ictx,
|
ictx,
|
||||||
creds,
|
creds,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
service,
|
service,
|
||||||
dc,
|
dc,
|
||||||
parentMetas,
|
folderMetas,
|
||||||
|
permissionIDMappings,
|
||||||
OneDriveSource,
|
OneDriveSource,
|
||||||
dest.ContainerName,
|
dest.ContainerName,
|
||||||
deets,
|
deets,
|
||||||
@ -96,10 +98,6 @@ func RestoreCollections(
|
|||||||
el.AddRecoverable(err)
|
el.AddRecoverable(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range folderMetas {
|
|
||||||
parentMetas[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreMetrics = support.CombineMetrics(restoreMetrics, metrics)
|
restoreMetrics = support.CombineMetrics(restoreMetrics, metrics)
|
||||||
|
|
||||||
if errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.Canceled) {
|
||||||
@ -128,19 +126,19 @@ func RestoreCollection(
|
|||||||
backupVersion int,
|
backupVersion int,
|
||||||
service graph.Servicer,
|
service graph.Servicer,
|
||||||
dc data.RestoreCollection,
|
dc data.RestoreCollection,
|
||||||
parentMetas map[string]Metadata,
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
source driveSource,
|
source driveSource,
|
||||||
restoreContainerName string,
|
restoreContainerName string,
|
||||||
deets *details.Builder,
|
deets *details.Builder,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
errs *fault.Bus,
|
errs *fault.Bus,
|
||||||
) (support.CollectionMetrics, map[string]Metadata, error) {
|
) (support.CollectionMetrics, error) {
|
||||||
var (
|
var (
|
||||||
metrics = support.CollectionMetrics{}
|
metrics = support.CollectionMetrics{}
|
||||||
copyBuffer = make([]byte, copyBufferSize)
|
copyBuffer = make([]byte, copyBufferSize)
|
||||||
directory = dc.FullPath()
|
directory = dc.FullPath()
|
||||||
folderMetas = map[string]Metadata{}
|
el = errs.Local()
|
||||||
el = errs.Local()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
|
ctx, end := diagnostics.Span(ctx, "gc:oneDrive:restoreCollection", diagnostics.Label("path", directory))
|
||||||
@ -148,7 +146,7 @@ func RestoreCollection(
|
|||||||
|
|
||||||
drivePath, err := path.ToOneDrivePath(directory)
|
drivePath, err := path.ToOneDrivePath(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderMetas, clues.Wrap(err, "creating drive path").WithClues(ctx)
|
return metrics, 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
|
||||||
@ -171,11 +169,11 @@ func RestoreCollection(
|
|||||||
ctx,
|
ctx,
|
||||||
drivePath,
|
drivePath,
|
||||||
dc,
|
dc,
|
||||||
parentMetas,
|
folderMetas,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
restorePerms)
|
restorePerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderMetas, clues.Wrap(err, "getting permissions").WithClues(ctx)
|
return metrics, 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,11 +183,16 @@ func RestoreCollection(
|
|||||||
service,
|
service,
|
||||||
drivePath,
|
drivePath,
|
||||||
restoreFolderElements,
|
restoreFolderElements,
|
||||||
colMeta)
|
dc.FullPath(),
|
||||||
|
colMeta,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings,
|
||||||
|
restorePerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics, folderMetas, clues.Wrap(err, "creating folders for restore")
|
return metrics, clues.Wrap(err, "creating folders for restore")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
folderMetas[dc.FullPath().String()] = colMeta
|
||||||
items := dc.Items(ctx, errs)
|
items := dc.Items(ctx, errs)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -199,11 +202,11 @@ func RestoreCollection(
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return metrics, folderMetas, err
|
return metrics, err
|
||||||
|
|
||||||
case itemData, ok := <-items:
|
case itemData, ok := <-items:
|
||||||
if !ok {
|
if !ok {
|
||||||
return metrics, folderMetas, nil
|
return metrics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
itemPath, err := dc.FullPath().Append(itemData.UUID(), true)
|
itemPath, err := dc.FullPath().Append(itemData.UUID(), true)
|
||||||
@ -223,6 +226,7 @@ func RestoreCollection(
|
|||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
folderMetas,
|
folderMetas,
|
||||||
|
permissionIDMappings,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
itemData,
|
itemData,
|
||||||
itemPath)
|
itemPath)
|
||||||
@ -259,7 +263,7 @@ func RestoreCollection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return metrics, folderMetas, el.Failure()
|
return metrics, el.Failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
// restores an item, according to correct backup version behavior.
|
// restores an item, according to correct backup version behavior.
|
||||||
@ -275,6 +279,7 @@ func restoreItem(
|
|||||||
restoreFolderID string,
|
restoreFolderID string,
|
||||||
copyBuffer []byte,
|
copyBuffer []byte,
|
||||||
folderMetas map[string]Metadata,
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
itemData data.Stream,
|
itemData data.Stream,
|
||||||
itemPath path.Path,
|
itemPath path.Path,
|
||||||
@ -341,6 +346,9 @@ func restoreItem(
|
|||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings,
|
||||||
|
itemPath,
|
||||||
itemData)
|
itemData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return details.ItemInfo{}, false, clues.Wrap(err, "v1 restore")
|
return details.ItemInfo{}, false, clues.Wrap(err, "v1 restore")
|
||||||
@ -361,6 +369,9 @@ func restoreItem(
|
|||||||
restoreFolderID,
|
restoreFolderID,
|
||||||
copyBuffer,
|
copyBuffer,
|
||||||
restorePerms,
|
restorePerms,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings,
|
||||||
|
itemPath,
|
||||||
itemData)
|
itemData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return details.ItemInfo{}, false, clues.Wrap(err, "v6 restore")
|
return details.ItemInfo{}, false, clues.Wrap(err, "v6 restore")
|
||||||
@ -408,6 +419,9 @@ func restoreV1File(
|
|||||||
restoreFolderID string,
|
restoreFolderID string,
|
||||||
copyBuffer []byte,
|
copyBuffer []byte,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
|
itemPath path.Path,
|
||||||
itemData data.Stream,
|
itemData data.Stream,
|
||||||
) (details.ItemInfo, error) {
|
) (details.ItemInfo, error) {
|
||||||
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix)
|
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix)
|
||||||
@ -445,7 +459,10 @@ func restoreV1File(
|
|||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
itemID,
|
itemID,
|
||||||
meta)
|
itemPath,
|
||||||
|
meta,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
||||||
}
|
}
|
||||||
@ -463,6 +480,9 @@ func restoreV6File(
|
|||||||
restoreFolderID string,
|
restoreFolderID string,
|
||||||
copyBuffer []byte,
|
copyBuffer []byte,
|
||||||
restorePerms bool,
|
restorePerms bool,
|
||||||
|
folderMetas map[string]Metadata,
|
||||||
|
permissionIDMappings map[string]string,
|
||||||
|
itemPath path.Path,
|
||||||
itemData data.Stream,
|
itemData data.Stream,
|
||||||
) (details.ItemInfo, error) {
|
) (details.ItemInfo, error) {
|
||||||
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix)
|
trimmedName := strings.TrimSuffix(itemData.UUID(), DataFileSuffix)
|
||||||
@ -511,7 +531,10 @@ func restoreV6File(
|
|||||||
service,
|
service,
|
||||||
drivePath.DriveID,
|
drivePath.DriveID,
|
||||||
itemID,
|
itemID,
|
||||||
meta)
|
itemPath,
|
||||||
|
meta,
|
||||||
|
folderMetas,
|
||||||
|
permissionIDMappings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
return details.ItemInfo{}, clues.Wrap(err, "restoring item permissions")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,13 +67,14 @@ func RestoreCollections(
|
|||||||
|
|
||||||
switch dc.FullPath().Category() {
|
switch dc.FullPath().Category() {
|
||||||
case path.LibrariesCategory:
|
case path.LibrariesCategory:
|
||||||
metrics, _, err = onedrive.RestoreCollection(
|
metrics, err = onedrive.RestoreCollection(
|
||||||
ictx,
|
ictx,
|
||||||
creds,
|
creds,
|
||||||
backupVersion,
|
backupVersion,
|
||||||
service,
|
service,
|
||||||
dc,
|
dc,
|
||||||
map[string]onedrive.Metadata{}, // Currently permission data is not stored for sharepoint
|
map[string]onedrive.Metadata{}, // Currently permission data is not stored for sharepoint
|
||||||
|
map[string]string{},
|
||||||
onedrive.SharePointSource,
|
onedrive.SharePointSource,
|
||||||
dest.ContainerName,
|
dest.ContainerName,
|
||||||
deets,
|
deets,
|
||||||
|
|||||||
@ -1264,6 +1264,13 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
|
|||||||
var (
|
var (
|
||||||
newFile models.DriveItemable
|
newFile models.DriveItemable
|
||||||
newFileName = "new_file.txt"
|
newFileName = "new_file.txt"
|
||||||
|
|
||||||
|
permissionIDMappings = map[string]string{}
|
||||||
|
writePerm = onedrive.UserPermission{
|
||||||
|
ID: "perm-id",
|
||||||
|
Roles: []string{"write"},
|
||||||
|
EntityID: suite.user,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Although established as a table, these tests are not isolated from each other.
|
// Although established as a table, these tests are not isolated from each other.
|
||||||
@ -1307,21 +1314,16 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
|
|||||||
driveItem := models.NewDriveItem()
|
driveItem := models.NewDriveItem()
|
||||||
driveItem.SetName(&newFileName)
|
driveItem.SetName(&newFileName)
|
||||||
driveItem.SetFile(models.NewFile())
|
driveItem.SetFile(models.NewFile())
|
||||||
err = onedrive.RestorePermissions(
|
err = onedrive.UpdatePermissions(
|
||||||
ctx,
|
ctx,
|
||||||
creds,
|
creds,
|
||||||
gc.Service,
|
gc.Service,
|
||||||
driveID,
|
driveID,
|
||||||
*newFile.GetId(),
|
*newFile.GetId(),
|
||||||
onedrive.Metadata{
|
[]onedrive.UserPermission{writePerm},
|
||||||
SharingMode: onedrive.SharingModeCustom,
|
[]onedrive.UserPermission{},
|
||||||
Permissions: []onedrive.UserPermission{
|
permissionIDMappings,
|
||||||
{
|
)
|
||||||
Roles: []string{"write"},
|
|
||||||
EntityID: suite.user,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoErrorf(t, err, "add permission to file %v", clues.ToCore(err))
|
require.NoErrorf(t, err, "add permission to file %v", clues.ToCore(err))
|
||||||
},
|
},
|
||||||
itemsRead: 1, // .data file for newitem
|
itemsRead: 1, // .data file for newitem
|
||||||
@ -1333,16 +1335,16 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
|
|||||||
driveItem := models.NewDriveItem()
|
driveItem := models.NewDriveItem()
|
||||||
driveItem.SetName(&newFileName)
|
driveItem.SetName(&newFileName)
|
||||||
driveItem.SetFile(models.NewFile())
|
driveItem.SetFile(models.NewFile())
|
||||||
err = onedrive.RestorePermissions(
|
err = onedrive.UpdatePermissions(
|
||||||
ctx,
|
ctx,
|
||||||
creds,
|
creds,
|
||||||
gc.Service,
|
gc.Service,
|
||||||
driveID,
|
driveID,
|
||||||
*newFile.GetId(),
|
*newFile.GetId(),
|
||||||
onedrive.Metadata{
|
[]onedrive.UserPermission{},
|
||||||
SharingMode: onedrive.SharingModeCustom,
|
[]onedrive.UserPermission{writePerm},
|
||||||
Permissions: []onedrive.UserPermission{},
|
permissionIDMappings,
|
||||||
})
|
)
|
||||||
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
||||||
},
|
},
|
||||||
itemsRead: 1, // .data file for newitem
|
itemsRead: 1, // .data file for newitem
|
||||||
@ -1355,21 +1357,16 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
|
|||||||
driveItem := models.NewDriveItem()
|
driveItem := models.NewDriveItem()
|
||||||
driveItem.SetName(&newFileName)
|
driveItem.SetName(&newFileName)
|
||||||
driveItem.SetFile(models.NewFile())
|
driveItem.SetFile(models.NewFile())
|
||||||
err = onedrive.RestorePermissions(
|
err = onedrive.UpdatePermissions(
|
||||||
ctx,
|
ctx,
|
||||||
creds,
|
creds,
|
||||||
gc.Service,
|
gc.Service,
|
||||||
driveID,
|
driveID,
|
||||||
targetContainer,
|
targetContainer,
|
||||||
onedrive.Metadata{
|
[]onedrive.UserPermission{writePerm},
|
||||||
SharingMode: onedrive.SharingModeCustom,
|
[]onedrive.UserPermission{},
|
||||||
Permissions: []onedrive.UserPermission{
|
permissionIDMappings,
|
||||||
{
|
)
|
||||||
Roles: []string{"write"},
|
|
||||||
EntityID: suite.user,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
||||||
},
|
},
|
||||||
itemsRead: 0,
|
itemsRead: 0,
|
||||||
@ -1382,16 +1379,16 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDriveIncrementals() {
|
|||||||
driveItem := models.NewDriveItem()
|
driveItem := models.NewDriveItem()
|
||||||
driveItem.SetName(&newFileName)
|
driveItem.SetName(&newFileName)
|
||||||
driveItem.SetFile(models.NewFile())
|
driveItem.SetFile(models.NewFile())
|
||||||
err = onedrive.RestorePermissions(
|
err = onedrive.UpdatePermissions(
|
||||||
ctx,
|
ctx,
|
||||||
creds,
|
creds,
|
||||||
gc.Service,
|
gc.Service,
|
||||||
driveID,
|
driveID,
|
||||||
targetContainer,
|
targetContainer,
|
||||||
onedrive.Metadata{
|
[]onedrive.UserPermission{},
|
||||||
SharingMode: onedrive.SharingModeCustom,
|
[]onedrive.UserPermission{writePerm},
|
||||||
Permissions: []onedrive.UserPermission{},
|
permissionIDMappings,
|
||||||
})
|
)
|
||||||
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
require.NoError(t, err, "add permission to file", clues.ToCore(err))
|
||||||
},
|
},
|
||||||
itemsRead: 0,
|
itemsRead: 0,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user