passing culture value inside wcf service hosted by iis - c#

Here is my code..
private IHelloWorld ChannelFactoryWebService()
{
ServiceEndpoint tcpEndPoint = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IHelloWorld)),
new NetTcpBinding(),
new EndpointAddress("net.tcp://myserver/CultureTest/Service.svc"));
ChannelFactory<IHelloWorld> factory = new ChannelFactory<IHelloWorld>(tcpEndPoint);
factory.Endpoint.Behaviors.Add(new CultureBehaviour());
return factory.CreateChannel();
}
[TestMethod] <-------------- FAILING TEST ----
public void ChangingCultureWASViaEndPointTest()
{
IHelloWorld client = ChannelFactoryWebService();
CultureInfo cultureInfo = new CultureInfo("ar-SA");
Thread.CurrentThread.CurrentCulture = cultureInfo;
client.Hello();
string culture = client.HelloCulture();
Assert.AreEqual("ar-SA", culture); <--- fails here.. I get en-US for culture
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
public class Server : IHelloWorld
{
#region IHelloWorld Members
public void Hello()
{
Console.WriteLine(string.Format("Hello world from the server in culture {0}", Thread.CurrentThread.CurrentCulture.Name));
}
public string HelloCulture()
{
string cultureName = Thread.CurrentThread.CurrentCulture.Name;
return cultureName;
}
#endregion
}
[ServiceContract]
public interface IHelloWorld
{
[OperationContract]
void Hello();
[OperationContract]
string HelloCulture();
}
public class CultureMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
private const string HeaderKey = "culture";
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader(HeaderKey, string.Empty);
if (headerIndex != -1)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(request.Headers.GetHeader<String>(headerIndex));
}
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
#endregion
#region IClientMessageInspector Members
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader(HeaderKey, string.Empty, Thread.CurrentThread.CurrentCulture.Name));
return null;
}
#endregion
}
public class CultureBehaviour : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CultureMessageInspector inspector = new CultureMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
CultureMessageInspector inspector = new CultureMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
public class CultureBehaviorExtension : BehaviorExtensionElement
{
// BehaviorExtensionElement members
public override Type BehaviorType
{
get { return typeof(CultureBehaviour); }
}
protected override object CreateBehavior()
{
return new CultureBehaviour();
}
}
Web config for the hosting site for Service looks as follows:
<endpointBehaviors>
<behavior name="Default">
<CultureExtension/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="CultureExtension" type="Extension.CultureBehaviorExtension, Extension, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
My question is, inside the following code when I go through the service hosted by website,
I am unable to get the culture value of ar-SA inside the Hello method of the service.
I tried my best to clarify the question here. Please let me know what else needs to be clarified.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
public class Server : IHelloWorld
{
#region IHelloWorld Members
public void Hello()
{
<--- problem here unable to get culture value of ar-SA here --->
Console.WriteLine(string.Format("Hello world from the server in culture {0}", Thread.CurrentThread.CurrentCulture.Name));
}
public string HelloCulture()
{
string cultureName = Thread.CurrentThread.CurrentCulture.Name;
Console.WriteLine("**************************hello**********************************");
Console.WriteLine(cultureName);
return cultureName;
}
#endregion
}

Its not really an answer why that particular code doesnt work, but why dont you just sent in the culture string and set it in your method on the WCF service?
Thats the way we usualy do it, or if its an authenticated user, just save it on the user so they can switch to any language regardless of computer config.

Related

Error control/handler (capture) with Behavior in wcf. Business layer

I have a project (backend) where it receives a call in the service layer, but it also makes calls to another API in the business layer.
I have added a behaviour to those calls so I do not have to make changes to several places in my code.
My Behaviour.config (the importatn is ExternalDependencyBehavior):
<?xml version="1.0" encoding="utf-8"?>
<behaviors>
<endpointBehaviors>
<!-- SPECIFIC BEHAVIOR EXTENSION FOR JSON URL DESERIALIZER-->
<behavior name="ServiceRESTBehavior">
<ConverterWebHttpBehaviourExtension />
</behavior>
<behavior name="ServiceSOAPBehavior" />
<behavior name="ExternalDependencyBehavior">
<ExternalClientBehavior/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="DefaultBehaviour">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
My code: (one method in business layer that call a other API external)
protected virtual Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201 RequestToVendor(VendorWebService vendor, string clientFiscalId, BasicSearchCriterion criteria)
{
Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201 result = new Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201();
string errorLogProvider = null;
try
{
using (Vendor.Sinacofi.SinacofiService.SNPV1201SoapClient client = new Vendor.Sinacofi.SinacofiService.SNPV1201SoapClient())
{
/*Credentials*/
result = client.ConsultaRutificacion(
client.ClientCredentials.HttpDigest.ClientCredential.UserName,
client.ClientCredentials.HttpDigest.ClientCredential.Password,
clientFiscalId,
criteria.Value
);
}
return result;
}
catch (AxrException)
{
throw;
}
catch (FaultException fex)
{
this.logProvider.WriteInErrorMode(GetType().Name, MethodBase.GetCurrentMethod().Name, fex);
throw this.exceptionProvider.Create(ErrorCode.---------, ExceptionType.BUSINESS, fex);
}
catch (Exception ex)
{
this.logProvider.WriteInErrorMode(GetType().Name, MethodBase.GetCurrentMethod().Name, ex);
throw this.exceptionProvider.Create(ErrorCode.-------------, ExceptionType.BUSINESS, ex);
}
}
In summary:
When I arrive at the call:
"client.ConsultaRutification"
My behaviour is executed, I add the code (2 files):
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace SIPE.Search.Helpers
{
/// <summary>
/// Implements methods that can be used to extend run-time behavior for an endpoint in either a client application.
/// </summary>
public class ExternalClientBehavior : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ExternalClientBehaviorClass();
}
public override Type BehaviorType
{
get
{
return typeof(ExternalClientBehaviorClass);
}
}
/// <summary>
/// JSON REST[GET] Converter Behavior
/// </summary>
private class ExternalClientBehaviorClass : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExternalClientMessageInspector clientInspector = new ExternalClientMessageInspector(endpoint);
clientRuntime.MessageInspectors.Add(clientInspector);
foreach (ClientOperation op in clientRuntime.Operations)
{
op.ParameterInspectors.Add(clientInspector);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ExternalClientMessageInspector(endpoint));
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
}
And file two:
namespace SIPE.Search.Helpers
{
/// <summary>
/// Intercepts send requests.
/// </summary>
public class ExternalClientMessageInspector : IClientMessageInspector, IParameterInspector, IErrorHandler
{
private ServiceEndpoint Endpoint { get; set; }
Dictionary<string, object> inputsParam;
public ExternalClientMessageInspector(ServiceEndpoint endpoint)
{
//empty
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
((Dictionary<string, string>)(System.Web.HttpContext.Current.Items[operationName])).Add("OutputParam", JsonConvert.SerializeObject(returnValue));
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
//code not necessary
}
public object BeforeCall(string operationName, object[] inputs)
{
// code not neccesary
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
Message copy = buffer.CreateMessage(); // Create a copy to work with
request = buffer.CreateMessage(); // Restore the original message
return copy.GetReaderAtBodyContents().Name;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// no implement
}
public bool HandleError(Exception error)
{
// no implement
return true;
}
public void NotifyError()
{
}
}
}
I need to be able to capture the errors, (for example 404 and others) of my calls within my behaviour.
I have tried to add several interfaces, but without success
The error is captured by the catch of the RequestToVendor method that is in the business layer.
But I need to be able to capture it in my behaviour.
thank you

C# windows service - expose web interface

I have created a web service that exposes web interface.
the service run in console mode and i see the web interface
public ServiceHost serviceHost = null; public ServiceHost serviceHost = null;;
private readonly TestService s;
public Service()
{
InitializeComponent();
s = new TestService();
}
protected override void OnStart(string[] args)
{
Logger.Info("Start event");
if (serviceHost != null)
{
serviceHost.Close();
}
// Create a ServiceHost for the CalculatorService type and
// provide the base address.
string baseAddress = "http://localhost:8000/Service";
serviceHost = new ServiceHost(typeof(Service1), new System.Uri(baseAddress));
serviceHost.AddServiceEndpoint(typeof(WindowsServiceTemplate.IService1),
new BasicHttpBinding(), baseAddress);
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
serviceHost.Description.Behaviors.Add(smb);
// Add MEX endpoint
serviceHost.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
// Open the ServiceHostBase to create listeners and start
// listening for messages.
serviceHost.Open();
s.Start();
}
protected override void OnStop()
{
Logger.Info("Stop event");
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
s.Stop();
}
protected override void OnShutdown()
{
Logger.Info("Windows is going shutdown");
Stop();
}
public void Start()
{
OnStart(null);
}
}
}
and app.config file :
<system.serviceModel>
<services>
<!-- Note: the service name must match the configuration name for the service implementation. -->
<service name="WindowsServiceTemplate.IService1" behaviorConfiguration="MyServiceTypeBehaviors" >
<!-- Add the following endpoint. -->
<!-- Note: your service must have an http base address to add this endpoint. -->
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mexHttpBinding" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
the configuration file inside app.config (console application project)
i am able to access
http://localhost:8000/Service
but when i try to call the test method
http://localhost:8000/Service/test
i get 404 error.
what am i missing?
Use Webhttpbinding to create the service and webservicehost to host service. I have made a demo, wish it is useful to you.
public partial class Service1 : ServiceBase
{
ServiceHost sh = new ServiceHost(typeof(MyService), new Uri("http://localhost:5900"));
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (sh.State==CommunicationState.Opened)
{
Log("Service open Fail");
}
else
{
WebHttpBinding webHttpBinding = new WebHttpBinding();
ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), webHttpBinding, "");
se.EndpointBehaviors.Add(new WebHttpBehavior());
sh.Open();
Log("Service is ready....");
}
}
protected override void OnStop()
{
if (sh.State==CommunicationState.Opened)
{
sh.Close();
Log("Service closed successfully");
}
}
private void Log(string text)
{
using (StreamWriter sw=new StreamWriter(#"D:\log.txt",true))
{
sw.WriteLine($"{text}----Time:{DateTime.Now.ToShortTimeString()}");
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return "Hello Stranger";
}
}
}
Install.
Result.
Here is an official document.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-basic-wcf-web-http-service
Feel free to let me know if there is anything I can help with.

BeforeSendRequest breakpoint not hit

i'm developing an small app, using a wcf service to provide data.
I want to add some headers to every message request, so i create an attribute inherit from IContractBehavior, then apply it to my service contract interface:
//SecuredServiceAttribute.cs
[AttributeUsage(AttributeTargets.Interface, Inherited = true)]
public class SecuredServiceAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageInspector());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new ServerMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
My ClientMessageInspector class as following:
// ClientMessageInspector.cs
public class ClientMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("smheader", "ns", "smvalue");
request.Headers.Add(header);
return null;
}
}
and my ServerMessageInspector:
// ServerMessageInspector.cs
public class ServerMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
int found = request.Headers.FindHeader("smheader", "ns");
if (found != -1)
{
string header = request.Headers.GetHeader<string>("smheader", "ns");
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
}
Now i apply SecuredServiceAttribute to the contract
// IService1.cs
[ServiceContract]
[SecuredService]
public interface IService1
{
[OperationContract]
string GetData();
}
The problem is: i put some breakpoint at BeforeSendRequest and AfterReceiveRequest but just AfterReceiveRequest is hit up, so that request.Headers.FindHeader("smheader", "ns") always return -1 because the BeforeSendRequest is not run.
Here is my service model section at server side:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
and client side:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:59814/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="Service.IService1"
name="BasicHttpBinding_IService1" />
</client>
Could you help me find what's wrong in my code?
Sorry i'm not good at English.

Find WCF service certificate, using several search criteria in config file

I'm wondering, is there any way to find WCF service certificate, using several search criteria in .config file?
E.g., if I want to find certificate by subject name, then my config will contain these lines:
<serviceCertificate findValue="host.domain.com"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName"/>
And what should I put in the config file, if I want to find a certificate by subject name and by application policy?
I know, that X509Certificate2Collection class allows this:
return store
.Certificates
.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.1", false)
.Find(X509FindType.FindBySubjectName, "host.domain.com", false)
.Cast<X509Certificate2>()
.SingleOrDefault();
And what about .config-files?
Write a custom service behavior and you will be able to provide your application policy AND subject name from configuration, executing two consecutive find on the certificate store.
public class ServerCertificateServiceBehavior : IServiceBehavior
{
private X509Certificate2 certificate;
public ServerCertificateServiceBehavior(StoreName storeName, StoreLocation storeLocation, string subjectName, string applicationPolicy)
{
X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
certificate = store
.Certificates
.Find(X509FindType.FindByApplicationPolicy, applicationPolicy, false)
.Find(X509FindType.FindBySubjectName, subjectName, false)
.Cast<X509Certificate2>()
.SingleOrDefault();
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
serviceHostBase.Credentials.ServiceCertificate.Certificate = this.certificate;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}
The ExtensionElement :
public class ServerCertificateServiceBehaviorExtensionElement : BehaviorExtensionElement
{
[ConfigurationProperty("applicationPolicy", IsRequired = true)]
public string ApplicationPolicy
{
get
{
return (string)base["applicationPolicy"];
}
set
{
base["applicationPolicy"] = value;
}
}
[ConfigurationProperty("subjectName", IsRequired = true)]
public string SubjectName
{
get
{
return (string)base["subjectName"];
}
set
{
base["subjectName"] = value;
}
}
[ConfigurationProperty("storeLocation", DefaultValue = 2)]
public StoreLocation StoreLocation
{
get
{
return (StoreLocation)base["storeLocation"];
}
set
{
base["storeLocation"] = value;
}
}
[ConfigurationProperty("storeName", DefaultValue = 5)]
public StoreName StoreName
{
get
{
return (StoreName)base["storeName"];
}
set
{
base["storeName"] = value;
}
}
public override Type BehaviorType
{
get { return typeof(ServerCertificateServiceBehavior); }
}
protected override object CreateBehavior()
{
return new ServerCertificateServiceBehavior(
this.StoreName,
this.StoreLocation,
this.SubjectName,
this.ApplicationPolicy);
}
}
The configuration becomes :
<behaviors>
<serviceBehaviors>
<behavior name="YourServiceBehaviorConfiguration">
<!-- ... -->
<serverCertificate storeLocation="LocalMachine"
storeName="My"
subjectName="host.domain.com"
applicationPolicy="1.3.6.1.5.5.7.3.1" />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="serverCertificate" type="Extensions.ServerCertificateServiceBehaviorExtensionElement, Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>

How do I restrict access to some methods in WCF?

I am a bit lost getting started with a simple WCF service. I have two methods and I want to expose one to the world and the second one I want to limit to certain users. Eventually I want to be able to use a client application to use the restricted method. So far I can access both methods anonymously:
C# Code
namespace serviceSpace
{
[ServiceContract]
interface ILocationService
{
[OperationContract]
string GetLocation(string id);
[OperationContract]
string GetHiddenLocation(string id);
}
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class LocationService : ILocationService
{
[WebGet(UriTemplate = "Location/{id}")]
public string GetLocation(string id)
{
return "O hai, I'm available to everyone.";
}
// only use this if authorized somehow
[WebGet(UriTemplate = "Location/hush/{id}")]
public string GetHiddenLocation(string id)
{
return "O hai, I can only be seen by certain users.";
}
}
}
Configuration
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>
How do I get started?
A lot of the answers I found were almost what I needed but not quite right. I wound up setting up ASP.net membership and implementing a custom attribute to pull an authorization header and process login as the request came in. All of the magic happens in BeforeCall and ParseAuthorizationHeader below:
public class UsernamePasswordAuthentication : Attribute, IOperationBehavior, IParameterInspector
{
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
public void AfterCall(string operationName, object[] outputs,
object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
var usernamePasswordString = parseAuthorizationHeader(WebOperationContext.Current.IncomingRequest);
if (usernamePasswordString != null)
{
string[] usernamePasswordArray = usernamePasswordString.Split(new char[] { ':' });
string username = usernamePasswordArray[0];
string password = usernamePasswordArray[1];
if ((username != null) && (password != null) && (Membership.ValidateUser(username, password)))
{
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username), new string[0]);
return null;
}
}
// if we made it here the user is not authorized
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.Unauthorized;
throw new WebFaultException<string>("Unauthorized", HttpStatusCode.Unauthorized);
}
private string parseAuthorizationHeader(IncomingWebRequestContext request)
{
string rtnString = null;
string authHeader = request.Headers["Authorization"];
if (authHeader != null)
{
var authStr = authHeader.Trim();
if (authStr.IndexOf("Basic", 0) == 0)
{
string encodedCredentials = authStr.Substring(6);
byte[] decodedBytes = Convert.FromBase64String(encodedCredentials);
rtnString = new ASCIIEncoding().GetString(decodedBytes);
}
}
return rtnString;
}
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
From there I just need to add my new attribute to the service contract entries. Any request to that method will require a valid authorization header or a Not Authorized response will be sent back with doing any further processing.
[ServiceContract]
interface ILocationService
{
[OperationContract]
string GetLocation(string id);
[OperationContract]
[UsernamePasswordAuthentication] // this attribute will force authentication
string GetHiddenLocation(string id);
}
Use the following steps to restrict access to specific Windows users:
Open the Computer Management Windows applet.
Create a Windows group that contains the specific Windows users to which you wish to give access. For example, a group can be called “CalculatorClients”.
Configure your service to require ClientCredentialType = “Windows”. This will require clients to connect using Windows authentication.
Configure your service methods with the PrincipalPermission attribute to require connecting users be members of the CalculatorClients group.
// Only members of the CalculatorClients group can call this method.
[PrincipalPermission(SecurityAction.Demand, Role = "CalculatorClients")]
public double Add(double a, double b)
{
return a + b;
}

Categories

Resources