Securing WCF services using ASP.NET Auth and a DomainService with Silverlight - c#

I have the basic ASP.NET authentication domain service running in my Silverlight app (the one that comes with the VS2010 Silverlight Business template).
How can I use the authentication that this grants to secure methods exposed by standard WCF services (also hosted in the same app in IIS)?

Ok, so this is how you do it, the standard WCF service needs a couple of attributes, and you need to assign the Thread.CurrentPrincipal:
[ServiceContract(Namespace = "")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
public Service1()
{
Thread.CurrentPrincipal = HttpContext.Current.User;
}
[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Role = "Registered Users")]
public string DoSomeWork()
{
return "working";
}
}

A good place to start is here: Security for WCF RIA Services. What you're looking for is the RequiresAuthentication attribute.
[RequiresAuthentication]
public Foo SomeMethodCall(object parameter1)
{
return service.GetResult(parameter1)
}

Related

Equivalent Odata WCF Service Functionality with OData Controller

In one project I was really close to implementing OData with a WCF Service and Entity Framework. With the WCF Service I was able to extend DataService and it was able translate my context to entities queryable by OData. I want to do the same thing except using an ODataController. Ideally, I'd like to have all of my Entity Sets accessible from one controller. Is this possible?
This is the approach that I've tried so far.
How do I bind multiple entity sets to one odata controller?
This the approach I used in my wcf service
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults = true)]
public class MyService : MSDataService.System.Data.Services.DataService<MyDataContext>
{
private SPWeb _web;
private string connectionString;
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public MyService() : base()
{
}
#endregion
#region Methods
protected override MyDataContext CreateDataSource()
{
return newMyDataContext();
}
public static void InitializeService(MSDataService::System.Data.Services.DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("MyEntity1", MSDataService::System.Data.Services.EntitySetRights.AllRead);
config.SetEntitySetAccessRule("MyEntity2", MSDataService::System.Data.Services.EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2;
config.DataServiceBehavior.AcceptProjectionRequests = true;
config.UseVerboseErrors = true;
config.EnableTypeAccess("*");
config.SetEntitySetPageSize("*", 1000);
}
#endregion
}
MyDataContext had DbSets for MyEntity1 and MyEntity2.
For the OData Controller, I would currently need two different controllers for each entity, but I would like to have one generic controller.
Sounds like you need RESTier:
Restier is the spiritual successor to WCF Data Services. Instead of
generating endless boilerplate code with the current Web API + OData
toolchain, RESTier helps you boostrap a standardized, queryable
HTTP-based REST interface in literally minutes. And that's just the
beginning.
Like WCF Data Services before it, Restier provides simple and
straightforward ways to shape queries and intercept submissions before
and after they hit the database. And like Web API + OData, you still
have the flexibility to add your own custom queries and actions with
techniques you're already familiar with.
https://github.com/OData/RESTier

Using AspNetCompatibility to run a client project from a WCF service

I have two projects - a WCF service that provides operations on a database, and an ASP.NET project running AngularJS that acts as a client to the service.
I would like to combine these into a single project. That is, when running the service, the interface (the ASP.NET AngularJS project) should appear.
I have seen some sources saying that AspNetCompatibilityMode can be used to do something like this, but I haven't seen anywhere how to actually specify a client.
Is this the right way to go about doing this? Is there a simpler way? Thanks in advance!
It is possible. I assume you want to expose existing WCF service in your ASP.NET web forms/mvc (whatever) type of project.
Steps:
1) make sure your ASP.NET project references the assembly in which is the WCF service implementation
2) change in your ASP.NET project your global.asax to:
using System.ServiceModel.Activation; // from assembly System.ServiceModel.Web
protected void Application_Start(Object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
void RegisterRoutes(RouteCollection routes)
{
routes.Add(new ServiceRoute("Services/Angular", new WebServiceHostFactory(), typeof(WCFNamespace.AngularService)));
}
This registers calls starting with the /Service/Angular prefix to be handled by your WCF service.
3) your WCF service should look like this
[ServiceContract]
public interface IAngularService
{
[OperationContract]
[WebGet(UriTemplate = "/Hello", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[Description("Returns hello world json object")]
HelloWorld GetHello();
}
[DataContract]
public class HelloWorld
{
[DataMember]
public string Message { get; set; }
}
Note the methods - they should be decorated with [WebGet] or [WebInvoke] methods since for Angular you want to build RESTfull wcf service. Also serialization/deserialization format is set to json.
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class AngularService : IAngularService
{
public HelloWorld GetHello()
{
return new HelloWorld { Message = "Hello from WCF. Time is: " +
DateTime.Now.ToString() };
}
}
Now you should be able to get json object if you type /Services/Angular/Hello into browser.
And finally as you've noted the WCF contract implementation (in this case class AngularService) has to be marked with attribute [AspNetCompatibilityRequirements] so the IIS can host it under ASP.NET web forms/MVC project.
Disclaimer: this is very naive implementation, in real world you probably would want to catch & log exceptions that happen in service and return them to client in form of json.

Web services and client DLL

I have a web service and a client DLL. The web service uses an Oracle database.
For testing the client DLL, I copied the web service and made it point to a test database. Then I copied the client DLL and added this test web service using "Add web reference".
What I would like to do is to use one web service and one client DLL but be able to tell the client DLL to use either use the test or production database rather than two identical web serivces and client DLLs.
Edit
I mis-stated the issue. What I need to do is use one client DLL and two web services (one production version, one development/test version) and be able to, somehow, tell the client DLL which web services to use.
This is a sample of how the web service, client DLL and client app are used:
public class DSSService : System.Web.Services.WebService
{
public DSSService()
{
}
[WebMethod(MessageName = "GetFacility", BufferResponse=true, Description = "blah.")]
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
Facility oFacility = ...;
...
return oFacility;
}
....
}
Client DLL:
namespace DSSConfig
{
string sWSURL;
public class Config
{
public Config()
{
}
public void SetWSURL(string sURL)
{
sWSURL = sURL;
}
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
DSSService Proxy = new DSSService();
proxy.Url = sWSURL;
Facility oFacility = Proxy.GetFacility(sFDBID, sZip, sFinNo);
return oFacility;
}
In client application, having DSSConfig DLL as reference:
DSSConfig oConfig = new DSSConfig();
oConfig.SetWSURL("http://myserver/WebService1/service.asmx");
oConfig.GetFacility("blah", "blah", "blah");
What you need to do is change the WEB Service to take a parameter that it will use to construct the connection string to the DB.
Then change client DLL to pass that parameter as part of the call or connection.
Then you can configure the Client DLL to using any technique you like to pass the parameter. My suggestion is perhaps derive a class from the generated proxy in the client DLL and use this in the client code.
Without specific implementation details I can't be more precise.

How to maintain Session in WCF Service

I have a WCF Service in which I want to maintain session for my Authentication method.
I have gone through from various articles and applied some of the below changes which are required to maintain session in WCF Service, as WCF not supported Session by default.
1- [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] in svc file.
2- [ServiceContract(SessionMode = SessionMode.Allowed)] in ServiceContract
3- Use the wsHttpBinding as basicHttpBinding not supported Session.
I am using WCFTestClient to call my service. I have checked the config of my TestClient and it is using basicHttpBinding, here is the cause of issue.
I am unable to implement the 3 point in my Service webconfig and also unable to change the config of my TestClient. Can anyone please guide me. Thanks
To solve this I implemented my own SessionHandler within the service.
a thread safe singleton class containing a Dictionary<Guid, SessionData>
Service Method: Guid RegisterClient(ClientName clientName) { /* add client to session */ }
Service Method: bool UnregisterClient(Guid clientGuid) { /* remove client from session */ }
Service Method: void DoThisOnServer(Guid clientGuid) { /* the service functionality */}
void CheckTimeout() { /* iterate over dictionary and remove out timed sessions */ }
Hints:
SessionData contains ClientName, TimeOfConnection, YourUsefulData
ClientName is a placeholder for IP-Adresse or some other initial identificator
Client has to register and all following operations are done only if the provided Guid exists in SessionHandler.

HTTPContext for webservices

HttpContext.Current.Request.ServerVariables["SERVER_PORT"]
HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"]
HttpContext.Current.Request.ServerVariables["SERVER_NAME"]
HttpContext.Current.Request.ApplicationPath
I want to access these value via a webservice -C#, whenever I call these values in webservice I get null for all of the above, where as above works for web pages (aspx).
As others have mentioned, you need to enable ASP.NET compatibility. You can also enable this via configuration if you don't want to limit your code via attributes like so:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Here's a great resource that helped explain to me the underlying functionality and trade-offs made by enabling compatibility mode.
What sort of web service are you using? asmx or wcf? They should work fine with asmx services but if you're using WCF, you'll need to add the following attribute to the method:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
have you tried to define your method with EnableSession at true?
[WebMethod(EnableSession = true)]
public string your_public_method(your_params)
{ [...] }
If it is a WCF web service you can do the following:
[AspNetCompatibilityRequirementsAttribute(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class FooBar : IFooBar
{
public void DoSomething()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
// Should get here now
}
}
}
The key is to add [AspNetCompatibilityRequirementsAttribute(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)].

Categories

Resources