Sharepoint Client in C# Share a folder with specific user - c#

We have a .NET web application that upload file to SharePoint using SharePoint.Client. The site connect to SharePoint using APP client ID and secret. The folder creation and file upload all work find. But it needs to share the folder (with subfolder where the document resides) with specific user in our organization, as it is done in SharePoint (see screenshot)
Share folder
with specific user
I have tried many ways but mainly these two:
Through role binding: Share specific folder in document library using SharePoint Client Object Model. But this gave me "Access Denied"
Through "SP.Web.ShareObject", passing similar parameters as in https://sharepoint.stackexchange.com/questions/279451/sharepoint-rest-api-shareobject-method . But the "SharingResult" it returns gave error "caller has no permission to grant permission".
Is there a way to accomplish this task?
Thanks!

Try this:
public void sendEmailWithLink(string email, string role = "read")
{
ClientContext context = new ClientContext(contextoURL);
SecureString passWordSegur = new SecureString();
foreach (var c in pw)
passWordSegur.AppendChar(c);
context.Credentials = new SharePointOnlineCredentials(user, passWordSegur);
Web web = context.Web;
string aux = "[{\"Key\":\"" + email + "\"}]";
string usuario = aux;
if (role == "contribute")
{
SharingResult result = Web.ShareObject((ClientRuntimeContext)context, folderPath, usuario, "role:1073741827", 0, true, true, false, "Carpeta o documento de SharePoint compartido contigo", "", true);
web.Context.Load(result);
context.ExecuteQuery();
}
else if (role == "full control")
{
SharingResult result = Web.ShareObject((ClientRuntimeContext)context, folderPath, usuario, "role:1073741829", 0, true, true, false, "Carpeta o documento de SharePoint compartido contigo", "", true);
web.Context.Load(result);
context.ExecuteQuery();
}
else if (role == "edit")
{
SharingResult result = Web.ShareObject((ClientRuntimeContext)context, folderPath, usuario, "role:1073741830", 0, true, true, false, "Carpeta o documento de SharePoint compartido contigo", "", true);
web.Context.Load(result);
context.ExecuteQuery();
}
else if (role == "read")
{
SharingResult result = Web.ShareObject((ClientRuntimeContext)context, folderPath, usuario, "role:1073741826", 0, true, true, false, "Carpeta o documento de SharePoint compartido contigo", "a", true);
web.Context.Load(result);
context.ExecuteQuery();
}
}
This code sends an email to the email adress specified in "email" which contains a link that gives permision to some folder (specified in the folder path).
For the documentation about the ShareObject function check this link: https://learn.microsoft.com/es-es/archive/blogs/vesku/external-sharing-api-for-sharepoint-and-onedrive-for-business
About the aux usage is because your supposed to create a PersonPicker (in the link I share you can see all the information) but this just happens to work fine with me.

Related

Send Sharing Link for Sharepoint Folder

I have been battling with this for a few days now... I am using CSCOM to connect to SharePoint. Everything is working fine, creating folders and uploading files. However, I now need to create a shared folder (parent level) link for external users and then initiate the email invite as per the "links giving access" not direct access. I can create and send an anonymous link but this is not what we are after.
string s = "password";
SecureString passWord = new SecureString();
foreach (var c in s)
passWord.AppendChar(c);
string siteURL = "siteurl";
string parentFolder = "parentfolder";
using (Microsoft.SharePoint.Client.ClientContext CContext = new Microsoft.SharePoint.Client.ClientContext(siteURL))
{
CContext.Credentials = new SharePointOnlineCredentials("s-eConnect#nzblood.co.nz",passWord);
var subFolders = CContext.Web.GetFolderByServerRelativeUrl(parentFolder).Folders;
CContext.Load(subFolders);
CContext.ExecuteQuery();
<<create sharing link for parent folder and send email to external user>>>
foreach (var subFolder in subFolders)
{
Console.WriteLine(subFolder.Name.ToString());
}
}
The above code iterates thru the sub folders of the parent, this is me just making sure I am getting the right data. But I can't seem to find anything that allows me to create the sharing link and send to an external user so they get an invite etc...
If I add in the following, it creates an invite but adds the user to the entire site via Direct Access ... not by link to the folder....
var users = new List<UserRoleAssignment>();
users.Add(new UserRoleAssignment()
{
UserId = "rhyndman#altara.net",
Role = Role.View
});
WebSharingManager.UpdateWebSharingInformation(CContext, CContext.Web, users, true, "You've been invited...", true, true);
CContext.ExecuteQuery();
Any help would be appreciated.
Many thanks
You could try to use DocumentSharingManager.UpdateDocumentSharingInfo method to send Sharing Link for Sharepoint Folder: https://learn.microsoft.com/en-us/previous-versions/office/sharepoint-csom/mt143107(v=office.15)
DocumentSharingManager.UpdateDocumentSharingInfo(CContext, folderabsoluteUrl, users, true, true, true, "You've been invited...", true, true);

How do I configure Discord role permissions using Discord.NET?

So I'm trying to code a Discord bot using Discord.NET API in C# and I came across a problem with configuring permissions for roles using the bot.
I'm trying to make a Text-Channel mute command by creating a "Muted" role. However, I cannot deny the permission for Send Messages. Here is what I have tried:
// Mute Command:
[Command("mute")]
[RequireUserPermission(GuildPermission.KickMembers)]
[RequireBotPermission(GuildPermission.KickMembers)]
public async Task Mute(IGuildUser user, int duration, string reason)
{
var role = await Context.Guild.CreateRoleAsync("Muted");
role.Permissions.SendMessages = false;
Threading.Sleep(duration);
await role.DeleteAsync();
}
Which does not work.
I have also tried replacing
role.Permissions.SendMessages = false;
with
role.Permissions.SendMessages.Equal(false);
which didn't work either. I've looked at the API documentation and still have not found a solution.
Any solutions to this?
The Permissions property in IRole only has a getter, meaning that you cannot set the Permissions objects or any of the properties within the Permissions object once it's been created. You may only get data from the object. You can see from the image below:
IRole definition
The CreateRoleAsync function has a GuildPermissions parameter (which is the 2nd parameter) which you can pass a custom GuildPermissions object into. When making a new GuildPermissions instance, you will need to use the constructor that takes in around 20 parameters which all set a different permission.
Here is some example code:
public void CreateRoleWithCustomPermissions()
{
var myCustomPermissions = new GuildPermissions(false,
false,
false,
false,
false,
false,
false,
false,
false, // this one is for sendMessages
false,
false,
false,
false,
false,
true,
false,
false,
true,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false);
var role = await Context.Guild.CreateRoleAsync("Muted", myCustomPermissions);
}
Here is a shorter version:
foreach (var channel in Context.Guild.Channels)
{
await channel.AddPermissionOverwriteAsync(role, OverwritePermissions.DenyAll(channel).Modify(viewChannel: PermValue.Allow, readMessageHistory: PermValue.Allow ....and so on));
}
Here the longer version:
bool rExist=false;
ulong roleID=0;
//Check if the role exist
foreach(var gRole in Context.Guild.Roles)
{
if(gRole.Name.Equals("role name here"))
{
rExist=true;
roleID=gRole.Id;
break;
}
}
if(!rExist)
{
//if the roles doesnt exist u create it and set the perms of the channels
var mRole = await Context.Guild.CreateRoleAsync(
"MuteRole", Discord.GuildPermissions.None,
Discord.Color.DarkTeal/*what ever color*/,false,null
);
try
{
await user.AddRoleAsync(mRole);
foreach (var channel in Context.Guild.Channels)
{
await channel.AddPermissionOverwriteAsync(mRole,
OverwritePermissions.DenyAll(channel).Modify(
viewChannel:PermValue.Allow, readMessageHistory: PermValue.Allow)
);
}
}
catch (Exception e)
{
//handel error if occures
}
}else
{
//if it exist just add it to the user
var role= Context.Guild.GetRole(roleID);
await user.AddRoleAsync(role);
//Check if the role is added to every channel. Same as above
}
NOTE:
It will add the role to EVERY channel in the server.

Examples how to save file from external .Net application to Sharepoint

I need to save files from the existing AngularJS/.NET application to Sharepoint. Most of the examples I see online is when applications reside on Sharepoint itself. How do I save files from outside?
I've been given a user access to our organization's Sharepoint site but no application user passwords. What do I need to request from administrators of SharePoint site to be able to write the code?
We can use CSOM C# code to upload file to SharePoint 2010 document library. We need use an admin user and password to pass the Credentials in the .NET application server.
public static void UploadFile(ClientContext context, string uploadFolderUrl, string uploadFilePath)
{
var fileCreationInfo = new FileCreationInformation
{
Content = System.IO.File.ReadAllBytes(uploadFilePath),
Overwrite = true,
Url = Path.GetFileName(uploadFilePath)
};
var targetFolder = context.Web.GetFolderByServerRelativeUrl(uploadFolderUrl);
var uploadFile = targetFolder.Files.Add(fileCreationInfo);
context.Load(uploadFile);
context.ExecuteQuery();
}
Usage
var siteUrl="http://sp2010";
var username="admin";
var password="xx";
var domainName="domain1";
using (var ctx = new ClientContext(webUri))
{
ctx.Credentials = new System.Net.NetworkCredential(username, password, domainName);
UploadFile(ctx,"Documents/folder1",#"c:\upload\test.docx");
}
The following article for your reference.
Uploading files using Client Object Model in SharePoint 2010

ASP.NET error getting user details from Active Directory

I am trying to build a registration section for a website (internal to my dept). Now to get new users registered, I built a form where user enters his employee id i.e. AD account name and then clicks a button to fetch his details. Which are later saved in database where registration requests are queued. Once those requests are approved by admin then only those users can use the application. Now the problem is that user is not logged in, so is it possible for non logged in user to fetch details from AD server. if it is then how.? Because when I tried the below listed code I am getting bad username or password error using FindOne function.
public string getProperties(string StaffCode, string property)
{
try
{
string result = "";
using (var de = new DirectoryEntry(_path))
using (var ds = new DirectorySearcher(de))
{
ds.Filter = string.Format("(sAMAccountName={0})", StaffCode);
ds.PropertiesToLoad.AddRange(new[] {
"sn", // last name
"givenName", // first name
"mail", // email
"telephoneNumber", // phone number
// etc - add other properties you need
});
var res = ds.FindOne();
if (res == null)
{
result = "noUserFound";
}
else
{
foreach (string propName in res.Properties.PropertyNames)
{
ResultPropertyValueCollection valueCollection = res.Properties[propName];
foreach (Object propertyValue in valueCollection)
{
if (propName == property)
{
result = propertyValue.ToString();
}
}
}
}
}
return result;
}
catch (Exception ex)
{
return "someErrorOccurred";
}
Please help me in overcoming this issue.
Thanks in advance
My guess is that the identity of the application pool you run this code under doesn't have enough priviledges to query the AD without authentication.
Specifically, start with replacing this constructor
using ( var de = new DirectoryEntry( _path ) )
with the one that takes admin's username/password in an explicit way
using ( var de = new DirectoryEntry( _path, username, password ) )
and make sure the username has enough priviledges to query the catalog.
If this works, you could possibly try to go back to the original version but you'd have to make sure the identity of the asp.net application pool has enough priviledges to query the AD but also, that the asp.net server is a part of the domain (if it is not, authentication without providing username/password in an explicit way will most likely not work).

Running a process with current credentials

I am developing an Umbraco intranet site where I call wkhtmltopdf.exe to create some pdfs. Those pdfs are using a main page for the content and two additional pages for header and footer. Things worked pretty fine as long as I had the site without authentication. We want to use our Active Directory accounts to login to the site and thus I have enabled windows authentication. The routine for running this is to click a button that process the page and either shows the pdf on the browser or downloads it. In any case it is the same process. In visual studio when running with debugging when it comes to the first part of the code (var p = ...) it throws an exception "Message = "No process is associated with this object." because it fails to authenticate. I can see that when I pause the code just after its execution and using the visual studio inspector. The method runs to the end but because of the error I mentioned before it produces a blank pdf. If I hardcode username and password then it works fine.
Site is running in my local dev enviroment in iis express. Since Windows Authentication is enabled when I browse to the site the first time I have to login. Wkhtmltopdf.exe is located in the local drive - it is not on the website. The initial setup is based on the method described here http://icanmakethiswork.blogspot.se/2012/04/making-pdfs-from-html-in-c-using.html Only users that are part of our Active Directory domain will have access to the website but since we use the same accounts to login to windows then windows authentication will do the trick :)
public static void HtmlToPdf(string outputFilename, string[] urls,
string[] options = null,
bool streamPdf = false,
string pdfHtmlToPdfExePath = "C:\\Program Files (x86)\\wkhtmltopdf\\bin\\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
{
throw new Exception("No input URLs provided for HtmlToPdf");
}
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : String.Join(" ", options)) + " " + urlsSeparatedBySpaces + " -",
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = string.Empty
}
};
p.Start();
var output = p.StandardOutput.ReadToEnd();
byte[] buffer = p.StandardOutput.CurrentEncoding.GetBytes(output);
p.WaitForExit(60000);
p.Close();
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "application/pdf";
if (!streamPdf)
{
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename='" + outputFilename + "'");
}
HttpContext.Current.Response.BinaryWrite(buffer);
HttpContext.Current.Response.End();
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilename, exc);
}
}
I tested this with LoadUserProfile = true but that didnt't help also. After reading throughout various forum posts the only suggested solution that I saw was to force loging in the process by using UserName, Password etc. But that is bad since the user is already logged in and we could/should use something like CredentialCache.DefaultCredentials .
A workaround that I came too was to use DefaultCredentials in requests to save the htmls locally where I can access them without a problem and create the pdfs but even that is a painstaking process, since i need to create printable css and javascripts and download them etc etc. This is my last solution which I have implemented at 80% but seems nasty also. Here is another code sample how I grab the webpages.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
var response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
So to sum up. Wkhtmltopdf fails to authenticate itself so that it can grab the desired pages and turn them to pdf. Is there any neat way to make the process able to authenticate itself with current user's credentials that I am logged in to the site so that it can access the pages?
I Use Rotativa a wrapper for Wkhtmltopdf.
To get it working on iis, I created a separate user account with enough access to run Wkhtmltopdf.exe. Then Pass the User Name & Password to Wkhtmltopdf with the switches.
public virtual ActionResult PrintInvoice(int id) {
var invoice = db.Invoices.Single(i => i.InvoiceId == id);
var viewmodel = Mapper.Map<InvoiceViewModel>(invoice);
var reportName = string.Format(#"Invoice {0:I-000000}.pdf", invoice.InvoiceNo);
var switches = String.Format(#" --print-media-type --username {0} --password {1} ",
ConfigurationManager.AppSettings["PdfUserName"],
ConfigurationManager.AppSettings["PdfPassword"]);
ViewBag.isPDF = true;
return new ViewAsPdf("InvoiceDetails", viewmodel) {
FileName = reportName,
PageOrientation = Rotativa.Options.Orientation.Portrait,
PageSize = Rotativa.Options.Size.A4,
CustomSwitches = switches
};
}
It appears the pages running within Wkhtmltopdf.exe run with the current users credentials, but the Wkhtmltopdf.exe itself needs rights to execute on the server.
This works on iis when deployed. On Cassini in VS2012 it works for me with no credentials, but in vs2013 on iis express I'm still having trouble when it comes to picking up resources like css & images.
Same solution to run over SSL:
Rotativa and wkhtmltopdf no CSS or images on iis6 over HTTPS, but fine on HTTP
Turn on ASP.NET Impersonation and spawn wkhtmltopdf under the context of the impersonated user.
Note: turning on ASP.NET impersonation is very likely to decrease performance.

Categories

Resources