WCF Impersonation Level Error on System.Data.OracleClient - wcf

I currently have a WCF service running in IIS7 and I have have added impersonation on each of the public web methods with the following:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void TestMethod(){}
When ever I call this method from my test client application I get the following error:
Could not load file or assembly 'System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=waeraewrar' or one of its dependencies. Either a required impersonation level was not provided, or the provided impersonation level is invalid.
I'm currently using Microsoft Enterprise Library 3.1, and .Net 4.0.
Sample code:
WcfService client = new WcfService();
client.TestMethod();

Try configuring the client to allow an impersonation level of impersonation. For example:
<system.serviceModel>
<client>
<endpoint address="http://xxxxx/Services/xxService.svc"
binding="wsHttpBinding"
contract="IServiceContract"
behaviorConfiguration = "ImpersonationBehavior" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="ImpersonationBehavior">
<clientCredentials>
<windows allowedImpersonationLevel = "Impersonation" />
</clientCredentials>
</behavior>
<endpointBehaviors>
</behaviors>
</system.serviceModel>
See this article for more on impersonation and delegation.

The error message suggests that the problem is that the impersonating user doesn't have access to the System.Data.OracleClient assembly DLL in the file system, and thus can't load it.
Can you not cause the System.Data.OracleClient assembly to be first loaded by code outside the service methods requiring impersonation... i.e. by code running with the IIS worker process identity. For example in your service instance constructor.
Once the assembly is loaded into the service's AppDomain, the service methods running under impersonation shouldn't need to do so again.

Related

WCF client app.config in Class Library

I have a Class Library project that contains a WCF client: the configuration and a lot of logic associated with it.
This DLL will be used by multiple client applications that have to consume exactly the same contract.
The issue is that the config resides in the DLL, but each client application has different endpoint address and client certificate.
How can I "inject" values from the client application into the attributes with '????????' value in the DLL's config?
<endpoint address="????????"
binding="customBinding"
bindingConfiguration="NewBinding0"
name="yyyy"
contract="bbbbb" />
...
<behavior name="TestBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My"
x509FindType="FindByThumbprint" findValue="????????" />
</clientCredentials>
</behavior>
...
Thanks :)
The config file in your DLL project will be used by Visual Studio, for example when updating service references: it isn't used at runtime.
Each client application that uses the DLL will need to have its own configuration file with the relevant configuration section in order to use the service at runtime.

MvcMiniProfiler profiling web app and lower layers

I have MiniProfiler set up and working in my ASP.NET MVC app. My controllers make calls via WCF to a BLL which in turn talks to the database. I would like to see profiling from the WCF service alongside the existing profiling I see from the web app. Is it a case of making MiniProfiler a parameter in all service calls?
In a recent release of the MvcMiniProfiler they added WCF support (version 1.8 or greater). This is a 3 step process to get this working:
Add References
First add references to the MvcMiniprofiler and MvcMiniProfiler.WCF in your UI layer and WCF layer via nuget (or download the source and compile your own).
Setup WCF Host
Second, within the web.config of the service host you have to add the miniprofiler as an endpoint behavior. All of the config sections belong in "configuration/system.serviceModel".
<endpointBehaviors>
<behavior name="miniProfilerBehavior">
<wcfMiniProfilerBehavior />
</behavior>
</endpointBehaviors>
Then add the behavior extension (Note the version number needs to match your version of the MvcMiniProfiler.WCF):
<extensions>
<behaviorExtensions>
<add name="wcfMiniProfilerBehavior" type="MvcMiniProfiler.Wcf.WcfMiniProfilerBehavior, MvcMiniProfiler.Wcf, Version=1.8.0.0, Culture=neutral" />
</behaviorExtensions>
</extensions>
Then setup the endpoints to use the profiler behavior you setup:
<services>
<service behaviorConfiguration="BaseBehavior" name="BSI.Something">
<endpoint address="" behaviorConfiguration="miniProfilerBehavior" binding="basicHttpBinding" bindingConfiguration="http" contract="BSI.ISomething"/>
</service>
</services>
Depends on your setup but I had to add one more web.config setting to run all managed modules for all requests. This config is in the root "configuration" section:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Setup WCF Client
Last, setup the wcf client to "turn on" the mvc profiler by doing much the same above.
Add the extension:
<extensions>
<behaviorExtensions>
<add name="wcfMiniProfilerBehavior" type="MvcMiniProfiler.Wcf.WcfMiniProfilerBehavior, MvcMiniProfiler.Wcf, Version=1.8.0.0, Culture=neutral" />
</behaviorExtensions>
</extensions>
Add a behavior:
<behaviors>
<endpointBehaviors>
<behavior name="wcfMiniProfilerBehavior">
<wcfMiniProfilerBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
Setup the endpoints to use that behavior:
<client>
<endpoint address="http://something/Something.svc" behaviorConfiguration="wcfMiniProfilerBehavior"
binding="BasicHttpBinding" bindingConfiguration="BasicHttpBinding_HTTP"
contract="BSL.ISomething" name="BasicHttpBinding_ISomething" />
</client>
And you're done!
Side Note:
How does the MvcMiniProfiler actually work over WCF?
Basically the client behavior sets up a SOAP header that tells the wcf host to turn on the profiler. It passes that header along which is read by the endpoint behavior on the WCF host side. It then turns the profiler on in the host. Lastly when the WCF host is replying back to the client it stuffs all the profiler goodness into the SOAP response header which is in turn read by the WCF client. Pretty ingenious.
That's one method, but in order to get the reference to the libraries you would have to add references in the lower layers for MvcMiniProfiler anyway.
What I did in this very same situation is to take advantage of the global access point that MiniProfiler provides as a singleton. So, I just added the reference in the lower levels (deleted the stuff relative to MVC, such as the views) and just used MiniProfiler.Current as if I were on the upper layers.
It works like a charm. :)

Service as ASMX and WCF

I have an existing web service (ASMX) that needs to be exposed as WCF as well. ASMX must remain and preferably with no change on the client. As per this I have configured as follows. The service layer is generated with CodeSmith and whilst I didn't write these services I know they are fine as they have been used in the wild for many years. The names have been changed to protect the innocent .. grin.
In the service layer there is an XXX.YYY.MyService class generated by CodeSmith which is double decorated with
[ServiceContract( Namespace = "http://XXX.YYY" )]
and
[WebService( Namespace = "http://XXX.YYY", Name = "MyService" )]
I have also created an empty interface XXX.YYY.IMyService which is implemented by MyService. At this point I can consume the ASMX service with no issues.
Now I add a .svc file to the service layer which contains ...
<%# ServiceHost Language="C#" Debug="true" Service="XXX.YYY.MyService" %>
... and I configure the service layer's web.config with ...
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="XXX.YYY.MyService">
<endpoint binding="basicHttpBinding" contract="XXX.YYY.IMyService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
If I build and then try and make a service reference in Visual Studio 2010 to the service, I see both .ASMX and .SVC versions of MyService. Expanding the .svc branch in the Add Service Reference dialog results in an error referring to an empty XML document.
If I examine the event log I get ...
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/39449526
Exception: System.ServiceModel.ServiceActivationException: The service '/System/MyService.svc' cannot be activated due to an exception during compilation. The exception message is: The contract name 'XXX.YYY.IMyService' could not be found in the list of contracts implemented by the service 'MyService'.. --->
... but MyService is marked as implementing IMyService ...
public partial class MyService : IMyService
I have also tried changing the contract attribute for the service to MyService instead of the interface. That works but for the client code breaks as any attempt to create an instance of the service fails as it is now an interface.
I hope that makes sense. Please feel free to ask anything extra. I have tried to be as detailed as possible.
(No IIS involved .. this is purely in Visual Studio 2010).
Thanks.
Your code implements IMyService are you sure that it is XXX.YYY.IMyService.
The answer for me was to move the [ServiceContract] and [OperationContract] declarations to the interface. This has fixed the issue for me.
HTH.

Azure WCF Service Consume Azure WCF Service

I current have a solution with an Azure WCF service and a Windows Phone 7 project. I can run the development fabric locally and browse to the url (http://127.0.0.1:81/API/V1.svc) of my service fine. When I do Add Service Reference from the Windows Phone application it will discover the service fine, but when I try to view the methods on the service I get the error "Unable to launch the ASP.NET Development Server because port '50149' is in use." If I click OK I get "There was an error downloading metadata from the address. Please verify that you have entered a valid address."
I don't quite understand why it is discovering it on port 50149 since I browse to it on port 81 but I tried using port 81 when adding the service and I got
There was an error downloading 'http://localhost:81/API/V1.svc'.
Unable to connect to the remote server
No connection could be made because the target machine actively refused it 127.0.0.1:81
Metadata contains a reference that cannot be resolved: 'http://localhost:81/API/V1.svc'.
There was no endpoint listening at http://localhost:81/API/V1.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Unable to connect to the remote server
No connection could be made because the target machine actively refused it 127.0.0.1:81
If the service is defined in the current solution, try building the solution and adding the service reference again.
Here is my service model section
<system.serviceModel>
<services>
<service name="DocDemon.API.V1">
<endpoint name="basicHttpBinding" binding="basicHttpBinding" contract="DocDemon.API.IV1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
</bindings>
</system.serviceModel>
Do I need to defined and end point in here?
Does it have something to do with the WP7 project and the Azure WCF being in the same solution? (Do I have to have the WCF running when I trying to add service reference from the WP7 app?)
I moved the WP7 Application into its own solution and then it was able to detect the web service fine when that application was running in the local DevFabric. The WP7 application was just unable to find it when they were in the same solution.
Have you looked at the WCF Azure Samples known issues on the MSDN Code Gallery? There's a subtlety around metadata and a behavior tweak needed. Hopefully this helps.
In my WCF running in Azure I configure endpoints in two places (my example defines a secure ssl endpoint on port 443):
1st time in web.config to define endpoints contracts:
<system.serviceModel>
<services>
<service
behaviorConfiguration="CustomValidationBehavior"
name="ServiceName">
<endpoint
binding="wsHttpBinding"
bindingConfiguration="MembershipBinding"
name="bindingName contract="InterfaceName" />
Afterwards, you also must make sure that Azure exposes your service thru its own endpoints in ServiceDefinition.csdef:
<InputEndpoints>
<InputEndpoint name="HttpsIn" protocol="https" port="443" certificate="CertName" />
</InputEndpoints>
You cannot use a reference to that port if it is not running, no metadata will be found.
I would say move your server project to IIS instead of Casini since that's where it'll run while on the Azure platform.
I did have some issues playing with Azure and Casini that did not happen on IIS.

Authenticating a WCF call from Silverlight using ADAM (AD LDS)

I have a Silverlight 4 client that invokes several WCF services. We want the communication to be encrypted using SSL (I have this part solved already) and that every call be authenticated against AD LDS (ADAM), do you have any simple example showing how to make this work? There's plenty of documentation on the oh-so-many WCF options but I haven't been able to find a simple working example of this particular (but I think very common) scenario (SSL encryption + ADAM authentication + Silverlight). Any help or pointers greatly appreciated.
You can use CustomUserNameValidator in WCF:
http://msdn.microsoft.com/en-us/library/aa702565.aspx
http://nayyeri.net/custom-username-and-password-authentication-in-wcf-3-5
and in Custom Validator's Validate Method you can query ADAM to authenticate user.
Regards.
Try this link on the Codeplex website, it seems like the setup and configuration for the scenario you've described. It provides a thorough checklist of all of the required settings:
Intranet – Web to Remote WCF Using Transport Security (Trusted Subsystem, HTTP)
If this isn't you exact scenario, take a look at the following section that may fill the gaps:
Application Scenarios (WCF Security)
The answer may depend on how you will handle permissioning, since you use ASP.net's membership provider for these functions.
If you want claims based authorization ADFS 1.0 (not 2.0) supports ADAM. If you want a STS that has more options try codplex's StarterSTS
If you want to use Role-Based Administration, try Enterprise Library from Microsoft P&P, the ASP.net membership provider, or direct COM access to Authorization manager (formerly known as AzMan)
I prefer & use the claims-based approach:
Use ActiveDirectory + ADFS 2.0
(why not use the built in fault tolerance and replication of AD?, ADAM is a pain)
Silverlight Implementation of Ws-Trust as documented here:
http://www.leastprivilege.com/UsingSilverlightToAccessWIFSecuredWCFServices.aspx
Edgar, I'm also interested in any results you had, I am at the same place you were in.
Shoaib, I have looked at this but I think it is less desirable than using just the .config via ActiveDirectoryMembershipProvider, as then you are just using off the shelf components, not writing your own security system.
EDIT:
I hope this helps someone. I can't belive there is not a good example of this on the internet. It is simple enough. As I said before this is superior to using a custom authentication system.
Using AD LDS (ADAM) authentication with Silverlight compatible WCF calls (non wsHttp)
Client side:
1) Invocations from Silverlight look like this, this works if you are using the Channel Factory too.
var client = new MyWCFServiceClient();
client.GetPersonCompleted += client_GetPersonCompleted;
client.ClientCredentials.UserName.UserName = username;
client.ClientCredentials.UserName.Password = password;
client.GetPersonAsync();
2) return values from server will have an Error property if the login fails. If the user lookup fails, the error is something confusing like “at least one security token could not be validated”. Since your server side code is not able to re-wrap this (as it's all happening in the web.config) it is better for your client code to catch System.ServiceModel.Security.MessageSecurityException and interpret it as a login failure, or check the InnerException message to make sure it is the "security token" thing.
void client_GetPersonCompleted(object sender, GetPersonCompletedEventArgs e)
{
if (e.Error == null)
{
// do stuff with e.Result;
}
if (e.Error is MessageSecurityException)
{
// Your login did not work
}
}
Server side:
1) The WCF service class must have
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] or Required
2) you have to set up your LDS instance with SSL enabled, which is tricky. See: h't't'p://erlend.oftedal.no/blog/?blogid=7
3) web config - need to:
Add the LDS connection string
Add ActiveDirectoryMembershipProvider
Add <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
change your custom binding to include <security authenticationMode="UserNameOverTransport"/> see http://msdn.microsoft.com/en-us/library/dd833059(VS.95).aspx
Example:
<configuration>
<connectionStrings>
<add name="ADConnectionString" connectionString="LDAP://myserver:[SSL port number]/[where your user are in LDS, in my case: ‘OU=ADAM Users,O=Microsoft,C=US’]" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<membership defaultProvider="MyActiveDirectoryMembershipProvider">
<providers>
<add
name="MyActiveDirectoryMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="[domain]\[username]"
connectionPassword="[plain text windows password]"
connectionProtection="Secure"
/>
</providers>
</membership>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehaviour">
<serviceMetadata
httpsGetEnabled="true"
httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="MyActiveDirectoryMembershipProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="myCustomBinding">
<security authenticationMode="UserNameOverTransport"/>
<!-- <binaryMessageEncoding /> this is optional, but good for performance-->
<httpsTransport />
</binding>
</customBinding>
</bindings>
<services>
<service name="MessageBasedSecurity.Web.MyWCFService" behaviorConfiguration="MyServiceBehaviour">
<endpoint address="" binding="customBinding" bindingConfiguration="myCustomBinding"
contract="MessageBasedSecurity.Web.MyWCFService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
I hope this helps someone. I welcome comments/improvements.