How to download FileVersion content from Sharepoint Online using CSOM - c#

Question
How do I read file content of the specific version?
What I have tried
I've tried:
var webClient = new WebClient() {Credentials = Context.Credentials })
webClient.DownloadString($"{Context.Url}{fileVersion.Url}");
but the Credentials is null. I've tried also UseDefaultCredential = true, but in both cases I'm getting 403 forbidden:
System.Net.WebException: 'The remote server returned an error: (403) Forbidden.'
I've also tried:
File.OpenBinaryDirect(sharepoint.Context, fileVersion.Url)
//fileVersion.Url == "_vti_history/512/ListName/filename.xml"
> Specified argument was out of the range of valid values.
> Parameter name: serverRelativeUrl
and also starting with /:
File.OpenBinaryDirect(sharepoint.Context, "/" + fileVersion.Url)
System.Net.WebException: 'The remote server returned an error: (404) Not Found.'
How do I create ClientContext:
I use sharepoint PnP to create and authenticate my ClientContext:
var authentication = new OfficeDevPnP.Core.AuthenticationManager();
Context = authentication.GetWebLoginClientContext(siteUrl);
I'm able to retrieve list of FileVersion:
var items = list.GetItems(camlQuery);
Context.Load(items);
Context.ExecuteQuery();
foreach (ListItem item in items)
{
sharepoint.Context.Load(item.Versions);
sharepoint.Context.Load(item.File.Versions);
sharepoint.Context.Load(item.File);
sharepoint.Context.ExecuteQuery();
foreach (var fileVersion in item.File.Versions)
{
ReadFile(fileVersion);
}
}
here are versions of my libraries:
<package id="Microsoft.SharePointOnline.CSOM" version="16.1.7018.1200" targetFramework="net461" />
<package id="SharePointPnP.IdentityModel.Extensions" version="1.2.2" targetFramework="net461" />
<package id="SharePointPnPCoreOnline" version="2.19.1710.2" targetFramework="net461" />

I've hacked the webclient authentication by stealing cookies from ClientContext:
var authentication = new OfficeDevPnP.Core.AuthenticationManager();
context = authentication.GetWebLoginClientContext(siteUrl);
webClient = new WebClient();
context.ExecutingWebRequest += StealCookieOnExecutingWebRequest;
void StealCookieOnExecutingWebRequest(object sender, WebRequestEventArgs e)
{
var cookies = e.WebRequestExecutor.WebRequest.CookieContainer;
webClient.Headers[HttpRequestHeader.Cookie] = cookies.GetCookieHeader(new Uri(SiteUrl));
context.ExecutingWebRequest -= StealCookieOnExecutingWebRequest;
}
I will not mark this as answer yet, since there might be better solution.

Related

Blazor Server, Sharepoint upload proxy settings

i have a problem uploading an xlsx file to sharepoint on the deployed version. In my IIS Express in the local pc i can upload the document without any problem. When i deploy the application the server gives me an exception "No connection could be made because the target machine actively refused it. [::ffff:127.0.0.1]:9000 (127.0.0.1:9000)". The Server uses a proxy server in order to execute the web request. I tried many solutions on the internet but nothing worked. I also developed a console application, which was able to upload the file without any further configuration.
This brings me to the conclusion, that the blazor server for some reason doesn't use the systems default proxy when it does the web request.
Here is my code:
public Task<bool> UploadDocument(byte[] fileBinaryArray, string fileName, string sharePointLink)
{
try
{
Debug.WriteLine("-----------------------------------" + sharePointLink);
var preparedLink = PrepareSharePointLink(sharePointLink);
Debug.WriteLine("-----------------------------------" + preparedLink);
ClientContext ctx = new PnP.Framework.AuthenticationManager().GetACSAppOnlyContext(_siteUrl, _clientId, _clientSecret);
/*ctx.ExecutingWebRequest += (sen, args) =>
{
System.Net.WebProxy myProxy = new System.Net.WebProxy("proxyIp", 8080);
args.WebRequestExecutor.WebRequest.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
args.WebRequestExecutor.WebRequest.Proxy = myProxy;
};*/
var folder = ctx.Web.GetFolderByServerRelativeUrl(preparedLink);
ctx.Load(folder);
ctx.ExecuteQuery();
folder.Files.Add(new FileCreationInformation
{
Overwrite = true,
Content = fileBinaryArray,
Url = fileName
});
ctx.ExecuteQuery();
return Task.FromResult(true);
}
catch(Exception e)
{
_eventlogService.LogError(e, _authenticationService?.UserName);
return Task.FromResult(false);
}
}

How to associate a certificate with a soap request in C#?

I am new to Web Services in general, and we are using .Net Framework 4.5.2, anyway I am trying to consume a web service that requires a certificate and a password.
I added the certificate gained from the providers in the project properties --> Resources --> file --> add, I also tried to use the SetCertificate() function but It seems to be a little complicated for me so I stick with loading the certificate from the properties as mentioned, however I already set all the binding setting as wanted, but somehow I am missing something, Here is my code:
string clientUrl = "some wsdl URL goes here";
BasicHttpsBinding binding = new BasicHttpsBinding
{
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue,
SendTimeout = new TimeSpan(0, 15, 0),
MessageEncoding = WSMessageEncoding.Text,
Security = {
Mode = BasicHttpsSecurityMode.Transport,
Transport = {
ClientCredentialType = HttpClientCredentialType.Certificate
}
}
};
ClientsClient testClient = new ClientsClient(binding, new EndpointAddress(new Uri(clientUrl)));
testClient.ClientCredentials.ClientCertificate.Certificate = LoadCertification();
private X509Certificate2 LoadCertification()
{
byte[] bytes = Properties.Resources.publicCert;
return new X509Certificate2(bytes, "password");
}
Note 1: The certificate extenstion is '.p12', It may be a list of certifications, if that is the case!, is it possible to pass them all?.
In the code I presented I am always getting The exception:
System.ServiceModel.ProtocolException: The 'Security' header from the namespace 'Some Http url goes here' not was understood by the recipient of the message. The message was not processed. The error usually indicates that the sender of the message has enabled a communication protocol that cannot be processed by the recipient. Verify that the client binding configuration is consistent with the service binding.
I tried to test the web service with "SOAP UI" and it worked, which made me sure that I am doing something wrong with the code, So I appreaciate any possible help that explains how to associate the certifcate in the code in the right way!.
EDIT:
in the .p12 file there are 3 certifications, which I tried to add also like this:
X509Certificate2Collection coll = LoadCertification();
int count = 0;
foreach (X509Certificate2 cert in coll)
{
testClient.ClientCredentials.ClientCertificate.Certificate = cert;
count++;// this variable is just to check the number of certificates
}
And I modified the loadCertification() method to look like this:
private X509Certificate2Collection LoadCertification()
{ string certPath = "C:/Users/ISA/Desktop/Progetti/Certificato e password/name.p12";
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Import(certPath , "password", X509KeyStorageFlags.DefaultKeySet);
return coll;
}

Excel Services in SharePoint Online

I have a working code using Excel Services published on on-premises SharePoint:
var _service = new ExcelService { UseDefaultCredentials = true };
Status[] _status;
var _sessionId = _service.OpenWorkbookForEditing("http://<sharepoint url>/<list>/<file>.xlsx", "en-US", "en-US", out _status);
ExcelService is a valid web reference pointing to http://<sharepoint url>/_vti_bin/excelservice.asmx
However, the same code doesn't work when using the Excel Services on SharePoint Online.
I am able to get the API version:
_service.GetApiVersion(out _status);
Excel Web Services (16.0)
But opening a workbook throws a SoapException:
We couldn't find the file you wanted.
I tried passing the credentials:
var _pass = new SecureString();
foreach (char _c in "<SP Online password>".ToCharArray()) { _pass.AppendChar(_c); }
var _cred = new SharePointOnlineCredentials("<SP Online login>", _pass);
var _service = new ExcelService { Credentials = _cred };
And the credentials seem to be valid, i.e. this code works:
// login test
using (ClientContext _sp = new ClientContext("<SP Online url>") { Credentials = _cred })
{
var _web = _sp.Web;
_sp.Load(_web);
_sp.ExecuteQuery();
Console.WriteLine(_web.Title);
}
What am I missing?
It seems it's an authentication issue but it's hard to tell since the error message is pretty opaque. How can I find out more details about the error?
Are Excel Services exposed only for specific licences?
Note, I am able to use Excel REST API but that doesn't work in my scenario (heavy editing).

How can I download artifacts from teamcity 8.1.2 using C# code

Using C# code I would like to download artifacts (zip file) from teamcity.
Based on the TC documentation (https://confluence.jetbrains.com/display/TCD8/Accessing+Server+by+HTTP and ) I wrote this code
string artifactSource = #"http://testuser:testpassword#teamcity.mydomain/httpAuth/downloadArtifacts.html?buildTypeId=ExampleBuildType&buildId=lastSuccessful";
using(WebClient teamcity = new WebClient())
{
teamcity.DownloadFile(artifactSource, #"D:\Downloads\1.zip");
}
In Visual Studio i got:
An unhandled exception of type 'System.Net.WebException' occurred in System.dll
Additional information: The remote server returned an error: (401) Unauthorized.
When I type url into browser I get correct response (file is ready to download).What I am doing wrong? Should I do authorization differently?
The following code achieves what you have described:
var artifactSource = #"http://teamcity.mydomain/httpAuth/downloadArtifacts.html?buildTypeId=ExampleBuildType&buildId=lastSuccessful";
using (var teamcityRequest = new WebClient { Credentials = new NetworkCredential("username", "password") })
{
teamcityRequest.DownloadFile(artifactSource, #"D:\Downloads\1.zip");
}
As you can see, I've taken out the username and password and passed them to Credentials property of the WebClient instead.
I would also recommend consider guest authentication if you have your guest account enabled in TeamCity (which I do in my company). This allows you to not use any credentials at all. In this case you need to change "httpAuth" in the url to "guestAuth" and the code becomes
var artifactSource = #"http://teamcity.mydomain/guestAuth/downloadArtifacts.html?buildTypeId=ExampleBuildType&buildId=lastSuccessful";
using (var teamcityRequest = new WebClient())
{
teamcityRequest.DownloadFile(artifactSource, #"D:\Downloads\1.zip");
}
I hope this helps.

Access SharePoint online using client object model- Forbidden error

I tried to Create a new list item using client object model. I have created an asp.net application to do the task. It works if I pass the URL of SharePoint server which is installed in my machine.
But if I give my SharePoint online URL it is not working as below code shows. I get "The remote server returned an error: (403) Forbidden. " error.
Any idea?
ClientContext context = new ClientContext("https://xxx.sharepoint.com/SitePages/");
List announcementsList = context.Web.Lists.GetByTitle("Announcements");
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
Microsoft.SharePoint.Client.ListItem newItem = announcementsList.AddItem(itemCreateInfo);
newItem["Title"] = result.City;
newItem["Body"] = result.State;
newItem.Update();
context.ExecuteQuery();
if you are trying to get a Context object from SharePoint Online you have to put in the right Credentials, as for SharePoint Online you should use the SharePointOnlineCredentials Class
A possible Authentication Method can be look like this:
private void AutheticateO365(string url, string password, string userName)
{
Context = new ClientContext(url);
var passWord = new SecureString();
foreach (char c in password.ToCharArray()) passWord.AppendChar(c);
Context.Credentials = new SharePointOnlineCredentials(userName, passWord);
var web = Context.Web;
Context.Load(web);
Context.ExecuteQuery();
}
I would imagine you just have to supply your login credentials and it should work:
clientContext.Credentials = new NetworkCredential("Username", "Password", "Domain");
You'll need to including System.Net:
using System.Net;

Categories