I would like to create some sort of authentication attribute and attach it to various OperationContracts. Inside this attribute, it would check for an authentication token and make sure its still valid before the OperationContract is run.
What's the best way to implement this on the .net platform? Does wcf have any special attributes that already do this type of functionality? What I'm picturing is something similar to the attributes you can attach to MVC controllers that will perform operations before actions are run.
In case it's relevant, I am using WCF to create SOAP web services that will be consumed by clients on various platforms that support SOAP.. not just WCF clients
Here's some code to clarify what I'm trying to do:
interface:
[ServiceContract]
public interface IService
{
[OperationContract]
string ValidateUser(string username, string password);
[OperationContract]
string GetDataAndAuthInCode(string authtoken);
[MyAuthorizationAttribute]
[OperationContract]
string GetDataAndAuthWithAttribute(string authtoken);
}
implementation:
public class Service : IService
{
public string ValidateUser(string username, string password)
{
if (!Membership.ValidateUser(username, password))
throw new Exception("invalid user...");
else
return GenerateAuthToken(username);
}
public string GetDataAndAuthInCode(string authtoken)
{
if (!IsAuthTokenValid(authtoken))
throw new Exception("Auth token invalid expired");
else
return GetData();
}
public string GetDataAndAuthWithAttribute(string authtoken)
{
return GetData();
}
}
Looks like this is what I'm looking for.. "Custom Behaviors":
http://msdn.microsoft.com/en-us/magazine/cc163302.aspx#S7
Related
I am looking for something like the AuthorizeAttribute in MVC, something I can use like this:
[WebGet(UriTemplate = "data/{spageNumber}")]
[WebCache(CacheProfileName = "SampleProfile")]
[WcfAuthorize]
public IEnumerable<SampleItem> GetCollection(String spageNumber)
{
Int32 itemsPerPage = 10;
Int32 pageNumber = Int32.Parse(spageNumber);
return Enumerable.Range(pageNumber * itemsPerPage, itemsPerPage)
.Select(i => SampleItem.Create(i));
}
That WcfAuthorizeAttribute, will try to authenticate the user with FormsAuthentication, and set the context's IPrincipal, or return a HTTP 401 Unauthorized.
I have tried with a IOperationBehavior, but I gets executed in the first method, whichever it be, not in the method I have set the attribute.
How can this be achieved in WCF REST?
Regards.
PS: I have seen the RequestInterceptor example in the Starter Kit, but what I want is put it in some methods only, and the example looks like a filter you execute in all the operations.
You can use AOP to achieve this. I have used PostSharp as an AOP tool to achieve this functionality. You can also find a sample on their website. The OnMethodEntry gets executed before a method (that is decorated with this attribute) is executed and you can perform your validation there.
I did a quick sample to test this and it worked.
[Serializable]
[ProvideAspectRole(StandardRoles.Security)]
public class WcfAuthorizeAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
//extract forms authentication token here from the request and perform validation.
}
}
And you could decorate your WCF methods like below.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service1
{
[WcfAuthorize]
[WebGet(UriTemplate = "")]
public List<SampleItem> GetCollection()
{
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}
I have the following operation contract method:
[OperationContract]
MyOutputMessageType DeleteVRequest(DeleteVRequest type);
[MessageContract]
public class DeleteVRequest
{
[MessageHeader(Name = "UserId")]
public Guid UserId;
[MessageHeader(Name = "Password")]
public String Password;
[MessageHeader(Name = "Version")]
public String Version;
[MessageBodyMember]
public Guid Id;
}
[MessageContract]
public class MyOutputMessageType
{
[MessageBodyMember]
public string Response;
}
My question is how are the soap header values of the contract (userid,password,version) set for the call on the client side? If I write a header value using Add on the outgoingmessageheaders it does not seem to map to the value in the messagecontract. So for instance if I add a UserId value to the headers, it does not seem to see that within the DeleteVRequest method.
Thanks for any help, I've really been struggling with SOAP header reader/writing in SOAP.
If the client side is using .NET, the service proxy will take in a message contract rather than the data contract, and you can set the header properties as you would any other properties.
Otherwise, you can using the OperationContextScope to set header values manually:
using(OperationContextScope context = new OperationContextScope(proxy.InnerChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("HeaderName", "HeaderNamespace", "SomeValue"));
//Make your proxy calls here
}
I have a WCF REST Service:
[ServiceContract]
public IService
{
[WebGet]
[OperationContract]
Data GetData(UserInfo userInfo);
}
UserInfo is a class:
public class UserInfo
{
public string UserName { get; set; }
public string Password { get; set; }
}
I want UserName and Password properties be filled from specific HTTP Headers but not from request body. How could I implement this?
WCF offers some extensibility points which could be helpful for this scenario. I suggest you should check IParameterInspector or IOperationInvoker. Both can work with input parameters. Then you will have to create IOperationBehavior as Attribute and mark your method with this attribute. But I expect this scenario will have one more bigger problem. You are using WebGet and complex type and you don't have any Uri template which will map to parameters - it is not possible. WebGet operations can use only parameters with basic types and when you create such parameter in operation it has to be mentioned in Uri template or exception is fired.
I am interested in impersonating well-known Web Services and Wcf Services for integration test purposes. To this end, I would like to capture service metadata, auto-generate service stubs, and host service stubs in a self-hosted environment.
Following this article here, I am able to obtain remote Wcf Service metadata and generate contracts. However, I am having some difficulty doing the same for remote Asmx Web Services.
I have a set of mickey-mouse solutions for vetting this out.
My Asmx solution contains a default "Hello World" web service, found below
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class SimpleAsmxService : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld () { return "Hello World"; }
}
My Wcf solution contains a default "Hello World" service, also found below
[ServiceContract]
public interface ISimpleWcfService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
public class CompositeType
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
public class SimpleWcfService : ISimpleWcfService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Finally, the little console-that-could looks like
class Program
{
public const string UrlWcf =
"http://localhost:8731/Design_Time_Addresses/SimpleWcfService/mex";
public const string UrlAsmx =
"http://localhost:1803/SimpleAsmxService.asmx?WSDL";
static void Main(string[] args)
{
EndpointAddress mexAddress = new EndpointAddress (UrlWcf);
MetadataExchangeClient mexClient =
new MetadataExchangeClient (mexAddress);
mexClient.ResolveMetadataReferences = true;
// NOTE: blows up if we use UrlAsmx
MetadataSet metaSet = mexClient.GetMetadata ();
WsdlImporter importer = new WsdlImporter (metaSet);
Collection<ContractDescription> contracts =
importer.ImportAllContracts();
}
}
It seems to me that I should be able to pull Wsdl from a well-known Asmx Web Service and generate contracts [and from contracts to code], but cannot seem to contort the preceding sample to do so. Any help would be much appreciated,
Thanks!
NOTE: the error generated when invoking MetadataSet metaSet = mexClient.GetMetadata(); above is a System.InvalidOperationException with message of
Metadata contains a reference that cannot be resolved : 'http://localhost:1803/SimpleAsmxService.asmx?WSDL'
With a System.InvalidOperationException inner exception with message of
<?xml version="1.0" encoding="utf-16"?>
<Fault xmlns="http://www.w3.org/2003/05/soap-envelope">
<Code>
<Value>Sender</Value>
</Code>
<Reason>
<Text xml:lang="en">
System.Web.Services.Protocols.SoapException: Unable to handle request without a valid action parameter. Please supply a valid soap action.
at System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)
</Text>
</Reason>
</Fault>
The way to get it to work with an ASMX web service is to specify the MetadataExchangeClientMode
...
MetadataExchangeClient mexClient =
new MetadataExchangeClient (new Uri(), MetadataExchangeClientMode.HttpGet);
...
using MetadataExchangeClientMode.HttpGet for your ASMX services
and MetadataExchangeClientMode.MetadataExchange for your WCF services.
I am implementing a WCF service (Contract A) that will eventually make calls to a standalone service (Contract B) hosted by the client. At design-time when the client queries my service's WSDL to build its proxy, I'd like to include the WSDL for Contract B so the client can build its service around that. Unfortunately, I can't figure out how to inject Contract B into the WSDL emitted by the service. Since the contract is an interface and doesn't have the [DataContract] attribute I can't add it as a known type. Is there any other way to inject a contract into emitted WSDL?
Here's an example:
[ServiceContract]
public interface IServerService
{
[OperationContract]
void GiveTheServerMyServiceUri(string uri);
[OperationContract]
void TellAllClientsSomething(string message);
}
// THIS IS THE INTERFACE I WANT TO INCLUDE IN THE WSDL
[ServiceContract]
public interface IClientService
{
[OperationContract]
void ReceiveMessageFromServer(string message);
}
public class ServerService : IServerService
{
private List<string> knownClients;
public void GiveTheServerMyServiceUri(string uri)
{
knownClients.Add(uri);
}
public void TellAllClientsSomething(string message)
{
foreach (string clientUri in knownClients)
{
// 1. Create instance of ClientServiceProxy using client's uri
// 2. Call proxy.ReceiveMessageFromServer(message)
}
}
}
At first it seems that this is a textbook example of a duplex contract. However, for this particular application, for a variety of reasons, I need a bit more separation between client and server so I was hoping to just give the client an interface to implement (via the WSDL), let it host its own service, then just tell me the service's url.
I don't see that this makes sense. Unless your service is implementing the service contract of the other service, then don't do this.
On the other hand, your service can implement the other service contract, and become a client to the other service. It can then delegate calls to the other service contract to that other service.
I just tried this to make sure. I created a new WCF Service library project. This created a Service1 implementing IService1, with two operations. I modified the [ServiceContract] attribute to use a specific namespace (http://localhost/service1).
I then added a new service, which gave me Service2, implementing IService2, with a single operation (DoWork). I updated the [ServiceContract] to use http://localhost/service2/.
I then updated Service1 to implement IService2 as well as IService1, and to delegate IService2.DoWork to the Service2 service. I did also have to add a new endpoint implementing IService2, and I had to specify a relative address, so that the two would not conflict (since they were in the same project). Here's the result:
using System;
namespace WcfServiceLibrary1
{
public class Service1 : IService1, IService2
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public void DoWork()
{
Service2Reference.IService2 svc = null;
try
{
svc = new Service2Reference.Service2Client();
svc.DoWork();
}
finally
{
if (svc != null)
{
((IDisposable)svc).Dispose();
}
}
}
}
}