WCF Web Services - Multiple Hop impersonation on the same server - c#

I have 3 web services, all located on the same server.
My Client calls Service A, which impersonates the client to call Service B, and all is well.
Now, I want to impersonate the caller of service B (which is my username) to call Service C. When I use the same technique as before (AllowedImpersonationLevel = Impersonate, user.Impersonate()), The user doesnt get passed to service C. Instead, Service C sees the user as the user I am running it under in IIS (which is a UPN, not the standard NETWORK SERVICE account).
Is there anything special I need to do to get this working? Is this a delegation issue? (I thought it would not be delegation because they are all on the same server)
Thanks SO!

You require delegation in this scenario. The configuration you require is ImpersonationLevel.Delegation (set in config or code). Have a look at the WCF Security Guidance on codeplex it is a very good source. Be careful as achieving delegation, particularly in a production environment, requirements more than simply selecting the correct option in the config file. You need to ensure that the application you connect to, e.g. SQL server, are configured for delegation, and that certain infrastructure requirements are met within active directory and the like, such as service principal names (SPN).

You can try turning on ASP.Net Compatibility on Service C
In Web.cofig
<system.web>
<identity impersonate="true"/>
<authentication mode="Windows"/>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
In your service class
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
public string ExecuteRequest(string xmlRequest)
{
IRequestManager requestManager = new RequestManager();
return requestManager.ProcessRequest(xmlRequest);
}
}

I would have expected to have to use delegation since you are crossing process boundaries twice. Have you tried TokenImpersonationLevel.Delegation?

Related

Different Authentication mode in different host/domain ASP.NET Web.config

How can I set different Authentication Mode in Web.config in different host/domain name?
For example, I have 3 different environments:
Host 1. localhost (For local dev use)
Host 2. abc-test.com (For Stage)
Host 3. abc.com (For Production)
I would like to set the Host 1 and Host 2 with <authentication mode="Windows" />, but "none" for Host 3, as it will be run with Azure SignIn method.
Note:
I figured out when I set <authentication mode="Windows" /> in web.config and specify the if-else clause in controller to distinguish which domain name to retrieve the userIdentity, the "Non-Windows Auth" and Request.IsAuthenticated (For Azure SignIn) will not be working properly.
Hope one of you has experienced the same before and may provide me with the best solution.
Thanks in advance! :)

Adding ASMX service to IIS for use with Biztalk

I've created a local webservice (.asmx), that I want to add to IIS. The service needs to be called from a Send Adapter in Biztalk.
My project in Visual Studio is structured like so:
There's a single .asmx file, that contains a single web method, see code below:
public class LocalWebService : System.Web.Services.WebService
{
private BankConnectClient client;
[WebMethod]
public void TransferPayment()
{
ProcessDirectory("C:\\Test\\BankConnectTestFiles");
}
I'm not very familiar with IIS, so I don't know best approch to add this service to run on my localhost. I tried adding a new website and placed the project folder in C:\inetpub\wwwroot, which I then reference in IIS with the following settings:
But when I browse to the root http://localhost:61406/, I receive an HTTP Error 403.14.
What is the correct approach in deploying an asmx web service to IIS, to then call in Biztalk using either the WCF-Custom or WCF-BasicHttp adapter?
It seems that the problem has been solved. The service URL need the LocalWebService.asmx suffix.
Besides, as far as I know, BasicHttpBinding in the WCF aims to compatible with ASMX web service, why not try to create a WCF service with BasicHttpBinding. And this is also supported by the BizTalk.
I Have made a demo, wish it is useful to you.
VS template.
Add the following code snippets to the default webconfig.
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
<!--add the following line to support http protocol-->
<add binding="basicHttpBinding" scheme="http"/>
</protocolMapping>
Then publish the project to the IIS folder and add the http binding to the IIS site binding module. We might need to enable the WCF support.
Result.
Feel free to let me know if there is anything I can help with.

Can't call WCF service from WebApplication

I have a simple WCF SOAP service hosted at company's Data Center, having just one Method that receives a Guid (for identifying the request), and returns nothing (void). We use this method as a signal for the service to load its configuration from Database. The service is a self-hosted WCF using SOAP and it is running as a WindowsService.
This service is called from a ASP.NET Web Application, when the user changes configurations related to him. The database is updated, and the service is called to load its updated settings.
The problem is, this service suddenly can't be accessed anymore from the application in production. I get a 404 error, BUT when opening WSDL from browser inside the application's machine, it loads normally.
Trying to reproduce the problem, I created Console Application just to call it from my development machine and it works. BUT, if I try to call it from a new Web Application, another problem happens: "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond".
Also, if I make a call using SoapUI or Visual Studio's WCF Test Client, I can connect without any problem.
For all tests, I used the same approach to made the client: Create a Service Reference (Visual Studio generated code tool). The code used to call the service is something like that:
using (var client = new NotificatorClient())
{
var request = new NotifyRequest { RequestId = Guid.NewGuid() };
client.Notify(request); // Works from any project that is not a WebApplication
Label1.Text = "Notificated!";
}
For all clients, my system.ServiceModel section of Web.config is the same (working on anything that is not a WebApplication, as I said):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_INotificator">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://myservice.com"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_INotificator"
contract="MyAppNotificatorSvc.INotificator" name="BasicHttpBinding_INotificator" />
</client>
</system.serviceModel>
Also, here's my service contracts, if it helps to understand better any problem that could be happening.
[ServiceContract]
public interface INotificator
{
[OperationContract]
void Notify(NotifyRequest request);
}
[DataContract]
public class NotifyRequest
{
[DataMember(Name = "RequestId", Order = 0, IsRequired = true)]
public Guid RequestId { get; set; }
}
We are using HTTPS with TLS 1.2 as our security protocol over requests, and Itried to change it by calling this code on the WebApplication, but I still have the same problem.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
I know that this may sound like not really relevant information about the problem, but this is as far as I got until now. Does anyone came into this type of problem? Any tip on what could be happening?

How can I perform initialisation in a WCF service library that isn't self-hosted?

I've created a WCF service library with a simple 'hello world' test service and a properly configured App.config file, so that when I start my client application the WCF service is started via Visual Studio's built-in host. The service as it stands is working fine with the external configuration in my client.
I need to run some initialisation code to set up DI, data access, logging etc. I've written a console host that can do that, and the service itself will eventually be deployed as a Windows service, but I want to use the built-in host so that I don't have to manually restart the service during development.
Is there some way I can hook some code in the library to be called on startup?
I found another question about using a custom ServiceHostFactory to perform bootstrapping, which is set up via a *.svc file. *.svc files are part of a WCF Service Application, and can't be used directly by a WCF Service Library. I want to stick to using a service library for some flexibility with my implementation of the services and the eventual production hosting, but using a WCF Service Application would get the job done in a way that suits development (and would probably be easy enough to drop in to IIS for production hosting with an alternate set of configuration). So I figured that I just needed to create a WCF Service Application that acts as a host for the services in the service library, and performs the required initialisation.
I first created a new WCF Service Application, dropped the default service files created by VS, and added a reference to the existing service library and other dependencies.
In the service app's Web.Config file, under the <configSections>..</configSections> section (which is required to be the first node after the <configuration> tag), I added the <system.serviceModel> section for the service library (this can be extracted from the App.config file in the service library and edited to suit).
For example:
<system.serviceModel>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="BelfryImages.QueryService.Implementation.HelloWorld">
<endpoint address="HelloWorld" binding="wsHttpBinding" name="HelloWorld" contract="BelfryImages.QueryService.Contracts.IHelloWorld" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
</bindings>
</system.serviceModel>
Note that the BelfryImages.QueryService.Implementation.HelloWorld is the FQN of the service implementation (implementing class) of the service contract (interface) BelfryImages.QueryService.Contracts.IHelloWorld. I actually have these in two separate assemblies; BelfryImages.QueryService.Contracts.dll is the WCF Service Library.
I added a new blank .svc file to the service application, named to match the endpoint, such as HelloWorld.svc. Usually, adding a WCF Service to a WCF service application results in a .svc file and a .cs codebehind file, which contains the implementation of the service. I manually added just the .svc file with no codebehind and pointed it to the service library implementation:
<%# ServiceHost Language="C#" Debug="true" Service="BelfryImages.QueryService.Implementation.HelloWorld" %>
As it stood this was usable as a drop-in replacement for the VS-hosted service library, after first changing the client's service URL from http://localhost:XXXXX/QueryService/HelloWorld to http://localhost:XXXXX/HelloWorld.svc/HelloWorld.
I then created the ServiceHostFactory to perform initialisation for the service. I found an MSDN article (Hosting and Consuming WCF Services) that explains how to do this (Listings 5-6 and 5-7) - for my purposes I just added a simpler, general purpose CustomHostFactory class:
public class CustomHostFactory
: ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
// perform initialisation:
...
var serviceHost = base.CreateServiceHost(serviceType, baseAddresses);
return serviceHost;
}
}
This is then bound to the service within the .svc file by adding a Factory attribute:
<%# ServiceHost Language="C#" Debug="true" Service="BelfryImages.QueryService.Implementation.HelloWorld" Factory="BelfryImages.WcfService.CustomHostFactory" %>
The initialisation at the top of the CreateServiceHost() override is performed before the service is created as usual. This only seems to happen once per service, rather than once per call to the service, but there will be overhead for multiple services. That could probably made a one-off by using a static flag or similar. For the moment it seems to work fine.
Would it work to have the initialization code in the static constructor of the class that implements your service?
public class WCFService : IWCFService
{
static WCFService()
{
// do initializing here
}
}
This code would execute as the first call is made to the service.

I need to develop a WCF service that will only be exposed thru HTTP/HTTPS. How to use HTTP cookies?

First of all, I admit I'm a newbie in WCF. Still not out of the training wheels.
I was assigned to develop a WCF service, and part of the requirements is that a sort of "session token" needs to be passed with each request as an HTTP cookie. (Predictably, such token needs be generated in the HTTP response headers of a successful "logon" call in such service).
Is this straightforward?
Disclaimer: you're not really supposed to do any of this, because it's forcing a WCF service to behave as a web service. But if you need cookies, read on.
If all you need is the session id, you can get it from:
OperationContext.Current.SessionId
If you need cookies, you'll need to jump through some hoops. The gist of it is (1) set asp.net compatibility, and (2) reference HttpContext.Current properties.
Your service will need to use a wsHttpBinding (or another binding that supports sessions). If create your project to be a WCF service hosted in IIS, you'll get these by default. You'll also need to set asp.net compatibility in the config file.
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<bindings>
<wsHttpBinding>
<binding name="MyBinding" allowCookies="false" ... </binding>
</wsHttpBinding>
</bindings>
(see the link here for why I have allowCookies=false)
To enable sessions, on your WCF Service Contract, set the following
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IMyWcfService {...}
You may also want to set the ServiceBehavior on the service itself (PerSession is the default), and you'll need to set asp.net compatibility.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
public class MyWcfService : IMyWcfService {...}
Some relevant properties you then have access to:
// Gives you the current session id as a string
HttpContext.Current.Session.SessionID
// Indicates whether the service is using sessionless cookies
HttpContext.Current.Session.CookieMode
// Indicates whether the session id is stored in the url or in an HTTP cookie
HttpContext.Current.Session.IsCookieless
// The cookies themselves
HttpContext.Current.Request.Cookies
HttpContext.Current.Response.Cookies
// The session and cache objects
HttpContext.Current.Cache
HttpContext.Current.Session
A link on sessions in WCF Services:
http://msdn.microsoft.com/en-us/library/ms733040.aspx
HTH,
James
This topic on msdn could help you out http://msdn.microsoft.com/en-us/library/bb398778.aspx.
Also, this could help you out with hosting you`r WCF service inside IIS: http://msdn.microsoft.com/en-us/library/aa702682.aspx
and http://msdn.microsoft.com/en-us/library/bb332338.aspx (Hosting Using Internet Information Services)
This just answers your question partially, but will give you a head start with config.
In your config file under the service config section, create basic http bindings like that:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="myHttpBinding" allowCookies="true">
</binding>
</basicHttpBinding>
</system.serviceModel>
Then read up on wcf binding and endpoints configuration.

Categories