SharePoint DriveItem permissions. Is there a more simple way to copy folders along with permissions ?
Hello
I am using the Graph SDK to copy an Sharepoint folder to another location. The target-subfolders must have the same permissions as those from the sourcefolder. To make it work, I do a temporary removal of the SiteGroup permissions on the target subfolders, and at last I want to restore those permissions. Here I get the error "Invalid value for role"
The call to Invite throws an exception, when role is "owner", the exception message is : "Invalid value for role"
My workarround this is to rename "owner" to "write" - just to be enable to use the /invite. I really would like to get rid of this workarround.
I am also looking for an alternative method (more simple) of doing this sync of permissions.
Best regards, Stefan
My code follows here :
private async Task CopyTemplatePermissions(
DriveItem projectDir,
DriveItem templateDir)
{
try
{
var settings = settingsAndToken.Settings;
var templateSubFolders = await GetChildDriveItems(settings, templateDir);
var projectSubFolders = await GetChildDriveItems(settings, projectDir);
foreach (var templateSub in templateSubFolders)
{
var projectChild = projectSubFolders.FirstOrDefault(p => p.Name == templateSub.Name);
if (projectChild == null)
{
continue;
}
// Get permissions
var templateChildPermissions = await GetDriveItemPermissions(settings, templateSub);
var projectChildPermissions = await GetDriveItemPermissions(settings, projectChild);
var projectUserIds = projectChildPermissions.Select(pc => pc.GrantedToV2.User?.Id).ToList();
var templateHasSpecialPermissions =
templateChildPermissions.Any(p => !projectUserIds.Contains(p.GrantedToV2.User?.Id));
// First temporary remove all permissions not existing in the template
// ALL SiteGroup permissions must be removed in order to be able to use /invite
List<Permission> deletedPermissions = new List<Permission>();
foreach (var pcPerm in projectChildPermissions)
{
if (templateHasSpecialPermissions ||
templateChildPermissions.All(p => p.GrantedToV2.User?.DisplayName != pcPerm.GrantedToV2.User?.DisplayName))
{
await graphClient
.Drives[settings.DriveId]
.Items[projectChild.Id]
.Permissions[pcPerm.Id]
.Request()
.DeleteAsync();
deletedPermissions.Add(pcPerm);
}
}
deletedPermissions.ForEach(p => projectChildPermissions.Remove(p));
// Then apply permissions from template: Patch or Add (Invite)
foreach (var tcPerm in templateChildPermissions.Where(p => p.GrantedToV2.User != null ))
{
var pcPerm = projectChildPermissions.FirstOrDefault(p =>
p.GrantedToV2.User?.DisplayName == tcPerm.GrantedToV2.User.DisplayName);
tcPerm.Roles = ReplaceOwnerRoles(tcPerm.Roles);
// Update
if (pcPerm != null)
{
var pcRolesStr = string.Join(";", pcPerm.Roles.OrderBy(p => p));
var tcRolesStr = string.Join(";", tcPerm.Roles.OrderBy(p => p));
if (pcRolesStr != tcRolesStr)
{
var updateResult =
await graphClient
.Drives[settings.DriveId]
.Items[projectChild.Id]
.Permissions[pcPerm.Id]
.Request()
.UpdateAsync(tcPerm);
}
}
else
{
// Invite
var userId = tcPerm.GrantedToV2.User.Id;
var recipients = new List<DriveRecipient>() { new DriveRecipient() { ObjectId = userId } };
var inviteResult = await graphClient
.Drives[settings.DriveId].Items[projectChild.Id]
.Invite(recipients, requireSignIn: true, roles: tcPerm.Roles, sendInvitation: false)
.Request()
.PostAsync();
// TODO : Consider to reduce round-trips by grouping by (recipients, roles)
}
}
// Restore the temporary removed SiteGroups
foreach (var permission in deletedPermissions.Where(p => p.GrantedToV2.SiteGroup != null))
{
var recipients = new List<DriveRecipient>() { new DriveRecipient() { Alias = permission.GrantedToV2.SiteGroup.DisplayName } };
//var roles = ReplaceOwnerRoles(permission.Roles);
var roles = permission.Roles; // trying... nope, invite throws exception when role is 'owner'
var inviteResult = await graphClient
.Drives[settings.DriveId].Items[projectChild.Id]
.Invite(recipients, requireSignIn: true, roles: roles, sendInvitation: false)
.Request()
.PostAsync();
};
}
}
catch (Exception ex)
{
// logs the exception - not relevant here.
}
}
private List<string> ReplaceOwnerRoles(IEnumerable<string> roles)
{
var rolesList = roles.ToList();
for (int i = 0; i < rolesList.Count; ++i)
{
rolesList[i] = rolesList[i].Replace("owner", "write");
}
return rolesList;
}