SOAP web service running on SharePoint - web-services

I have a SOAP web service running on my sharepoint box under the _layouts directory, and a thick client which uses that SOAP service. We have one sharepoint box that uses basic auth and another which uses client certificates. I need that SOAP service to update some list items in a document library. The problem I'm having is nothing seems to work unless I run within an elevated privileges block. Here is a code snippet of what I'm trying to do.
using (SPSite site = new SPSite(fileUrl))
using (SPWeb web = site.OpenWeb()) {
// web.CurrentUser is always null unless in elevated privileges block.
// do something with document library...
web.Files.Add(...); // fails with access denied unless in elevated privileges block.
}
I also tried "SPContext.Current.Web" but it returns null for "web.CurrentUser" even if I'm in an elevated privileges block.
I really can't use an elevated privileges block because the users complain that anything my SOAP service touches has a modified by system.
From the thick client we are using code like the following...
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestString);
if(basicAuth) {
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
else {
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certificate in store.Certificates) {
request.ClientCertificates.Add(certificate);
}
request.GetResponse();
We are manually constructing the SOAP request for various reasons.

If you want your webservice to properly integrate within the sharepoint context (i.e. being able to query the "current sharepoint user"), you should really deploy it to _vti_bin (the ISAPI subfolder of the 12 hyve), not to _layouts.
Discovery is a bit of a pain and requires manual tweaking of files (see the MSDN article on custom webservices within SharePoint for more information), but as you are hand-building your request anyway discovery should not be an issue.
[Edit] As an alternative, you can try acquiring the SPUserToken of the windows authenticated user,
SPUserToken token = web.AllUsers[WindowsIdentity.GetCurrent().Name].UserToken;
and then use this token to open the site and web as this user.

Related

Is there a REST API for TFS that functions without Azure DevOps?

I've been assigned to build a set of tools for DevOps for improving our turnaround time on building several branches. These tools will need to interact with our source control, which is currently running on Team Foundation Server. When I asked if we had access to Azure DevOps, which according to the tutorials I found online was needed to create an authentication token that could be used to access TFS through it's default REST API, I was told that we did not and had no plans to, because this company was an AWS organization.
Does anyone know if there's a way to access a REST or similar API for TFS if all you have access to is TFS running on AWS?
Yeah, as mentioned in the comment, you should use PAT to authenticate
TFS through Rest API. Username should be blank. PAT is the password. There are multiple related tutorials in google, a sample for your refer:
using System.Net.Http;
using System.Net.Http.Headers;
...
//encode your personal access token
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalAccessToken)));
ListofProjectsResponse.Projects viewModel = null;
//use the httpclient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://{accountname}.visualstudio.com"); //url of our account
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
//connect to the REST endpoint
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=1.0").Result;
//check to see if we have a succesfull respond
if (response.IsSuccessStatusCode)
{
//set the viewmodel from the content in the response
viewModel = response.Content.ReadAsAsync<ListofProjectsResponse.Projects>().Result;
//var value = response.Content.ReadAsStringAsync().Result;
}
}
Since you are using PAT with on-premise TFS server, make sure you already turned off Basic Authentication on server. Otherwise you will get an returned error.
We recommend you keep IIS Basic Authentication turned off at all times
when using Azure DevOps Server. Only if necessary should you enable
IIS Basic Authentication. When IIS Basic Authentication is enabled on
your windows machine, it prevents you from using personal access
tokens (PATs) as an authentication mechanism.
Source link.
Moreover, we do have a good integration with AWS. We could call AWS services from Azure DevOps/TFS side. Also be able to use AWSCLI & AWS Powershell Module. In case you need, for more details please take a look at this link-- AWS Tools for Microsoft Visual Studio Team Services/TFS

SOAP Web Service with Inno Setup

While using Inno Setup to create an installer a need as arisen to verify a user's credentials during the installation process. The application has been out in production for a while and the only need to move to an installer is due to support for NPAPI within web browsers being deprecated. There is already a way that the application verifies a user's credentials before launching the application that I'm trying to take advantage of, which is through a SOAP request. The verification process is just not making sure the user is authorized but also assigning them a token that prevents their information from having to be sent multiple times during the running of the application.
My question, is there a way to make the SOAP request to verify a user's credentials through during the installation process? If so, how would this be accomplished?
SOAP is just an XML over HTTP.
So you can use the WinHttpRequest class:
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
WinHttpReq.Open('GET', 'http://soapserver.example.com/', False);
WinHttpReq.SetRequestHeader('Content-Type', 'application/soap+xml;charset=UTF-8');
WinHttpReq.SetRequestHeader('SOAPAction', '...');
WinHttpReq.Send('<data/>');
{ WinHttpReq.ResponseText will hold the SOAP response }
See also HTTP POST request in Inno Setup Script.
To parse the SOAP response, you can use the Msxml2.DOMDocument class:
How to read and write XML document node values?

How to call SharePoint 2007 web services from Silverlight on FBA site

I have a Silverlight 5 app that runs on a SharePoint 2007 site. The Silverlight app gets a bunch of data from SharePoint lists using the Lists.asmx service. The main site is secured using NTLM security and Silverlight is able to successfully call the web services without having to eplicitly set anything to do with authentication. We extended the SharePoint site to a second domain that uses asp.net FormsBasedAuthentication. On this version of the site, the first web service call fails with a 403 Forbidden response and the exception:
System.ServiceModel.CommunicationException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound.
As I understand it, this is a generic message and not the true exception. Well, obviously we need the real exception to have any idea what the problem is. This MSDN page (Creating and Handling Faults in Silverlight) offers two solutions to getting the real exception.
The first is to modify your WCF service to return an alternate HTTP status code. This doesn't work because for one, it is not a WCF service, it's an .asmx web service so I can't add the suggested WCF behavior to modify the status code. Second, it's SharePoint's service so I can't do much to modify it anyway. Could I modify IIS to achieve an equivalent solution somehow?
The second solution is to register an alternative HTTP stack in the Silverlight application. I tried this out and found out the "real" exception was an authentication exception. So I went down dead ends trying to figure out out to get authenticated for about 24 hours only to finally find out that normally, the ASP.NET authentication cookie is passed with the service request, unless you are using the Client HTTP stack. So registering the client HTTP stack allowed me to see real exceptions, but it created its own exception which seems to only be fixed by not using the Client HTTP stack...
I believe have verified with Fiddler that the authentication cookie is being sent when using the default HTTP stack. I don't know if the Lists.asmx service is unable to use it and is giving an authentication error anyway, or if there is some other exception. How can I determine the cause of the "The remote server returned an error: NotFound." exception?
Well, this isn't a good answer to the quesion "How can I determine the cause..." but it's what worked for me. What I did was open up STSSOAP.dll, the assembly containing the Lists service implemention, in reflector. I copied the relevant code to implment my own GetListItems method in my own service. Luckily, the actual Lists.GetListItems method code was minimal and just called other SharePoint methods and even luckier, those methods & members are all declared public so I was able to do this. I then replaced Lists.asmx with my version on the server and attached the debugger to get some info. What I found was my authentication cookie was being used and HttpContext.Current.Session.User.Identity.IsAuthenticated was true. So it knew I was logged in. But I was still getting an authentication error which I could see farther down in SharePoint's code, was converted to a 403.
So I know asp.net considers me authenticated but SharePoint says I don't have permissions. But the service account the app pool is running under is a SP admin so why don't I have permissions? So next I inspected the SPContext.Current.Web.User; SharePoint's current user. It was null! In other words, as far as SharePoint is concerned, I'm still logged in as an anonymous user because I haven't explicitly logged in with a domain user, and an anonymous user doesn't have permissions on the list I'm accessing or most of the rest of the API. The answer by Sean McDonough to this quesion lays it out.
Basically I need to run the involved code with elevated privileges to get the code to run under the asp.net service account that I originally thought it was running under. If using the API, you can use the RunWithElevatedPrivileges delegate. But if you're calling the web services, you can't do that. The few options I could think of were:
Call the services on the base site that uses Windows Authentication. This would require me to embed the credentials in the client-side Silverlight application which is a security threat so I nixed this one.
Implement my own web service to get the data for me.
The web service could access the list using the SharePoint API and
RunWithElevatedPrivileges.
The web service could call the Windows Authentication site's services passing the current credentials or other embedded credentials.
For implementation ease, all I've done so far is the second option under number 2 and it's working. However, it is clunky and I may change to option 1 which I expect will also work.

Implementing web service security in Reporting Tools

I have a web reporting tool lets say Business Objects, Cognos, OBIEE, Crystal Reports. I want to display some data into the report which is coming from a Web Service. So i copy paste the Web service URL inside the report cell and i can access the data.
However this leaves a big security issue as i cannot authenticate the requestor. One thing which i can think of is checking the Http header request: referer property which is set by the reporting tool in my Web Service. This atleast ensures that the request has originated from my Reporting Application. Besides this i cannot see how i can authenticate a specific user.
Appending Username in the Web Service URL is also not an option because one report is used by many users. I would somehow want to access this specific user session and associate the web service request with this user session. Lets say both my Web Reporting tool and web service are running on the same Web Application Server. Is it possible to merge the Web Service Provider and my Reporting Application so that the session user name is available in the WebService ?

Microsoft sync framework Authentication

I'm very novice when it comes to web applications and ASP.
Recently, I've been experimenting with the Microsoft Sync Toolkit to synchronize databases over a OData web service.
The obvious question here is: Once the service is set up and published - so it is open for anyone knowing the URL - how to prevent unauthorized users from accessing this service.
Please note: Basic authentication of forms authentication - as far my little web development knowledge reaches - doesn't seem to be appropriate for this task, as it's not a web page that the client is trying to reach - where the page can display / or re-direct a logon request - it's a service that we are accessing here.
To make things more difficult, for the client-side syncing I'm using a 3rd party library/sync-provider that only accepts a URL for the service. So, there's no way (I think) I can experiment with incorporating login credentials inside a request header etc.
I assume the best bet would be embedding the login credentials inside the URL and use that for the 3rd party library.
Can somebody please direct me how to to set up such thing on the server? I would prefer to have somehow somewhere in the server-side code a place where I can check for the credentials and based upon it to proceed or abort (return 401) the service request.
I could not find any place where to hook such code into the sync service. Although somebody in MSDN suggested to handle the _OnBeginSyncRequest event, there is no way to access the web-request header from within that method.
Is there by-any-chance a global object accessible from everywhere from which I can access the request header? Could anyone please help with this?
And last, I would prefer a plain User / Password string pair. It should not necessarily (or rather not) have anything to do with windows or directory accounts. I would prefer in my code to simple check against plain strings, such if(userStr == "Authenticated user" && passwordStr == "Correct Password").
if you are using SOAP web service, you can use WS-Security usernametoken which adds your user name and password to the request header, otherwise you can add username and password as parameters in your webservice and then simply validate it on the server side. i.e.
instead of
bool SyncData(datatable)
it becomes
bool SyncData(datatable, username, password)
note for web service you will authenticate per call, if you want to do it per session, you need first login with username password, retrieves a token than on each subsequent call your service with the token.
You would also use SSL to secure the channel so username and password aren't transmitted as plain text.