Consuming ODATA Services from LightSwitch with Impersonation

In case you are still using LightSwitch and are not entirely relying on its internal features, you might have started using ODATA services as LightSwitch data sources.
This comes in especially handy when combined with the Microsoft Managed Extensibility Framework (MEF) which lets you create decoupled components that can be easily plugged into your core application.
However, as soon your ODATA controllers are decoupled from the LightSwitch server model (read ServerApplicationContext) and wrapped as ODATA data sources in LightSwitch you are locked out of LightSwitch’s permission and role based access control model (RBAC). This is because LightSwitch connects to your ODATA controllers via the identity of your web site (which is the AppPool identity).

But there is help: you can always impersonate a HttpWebRequest and LightSwitch lets you hook into that via one of the events in the DataServiceContext class.

The events are not accessible via the normal ‘Write Code’ drop-down box, instead you have to know the method signatures to override:

partial void YourDataServiceName_SendingRequest(ODataSendingState state)
partial void YourDataServiceName_ReceivedResponse(ODataReceivedState state)

The ODataSendingState is just a container object with a single (HTTP Web) Request property.

Inside these event handlers you simply set up the impersonation and revert back to self after completion (in fact for every request). The impersonation context will be saved in your data service class for the request.

So a simple code for impersonation could look like this:

using System.Data.Services.Client;
using System.Web;
using Microsoft.LightSwitch;
using System;

namespace LightSwitchApplication
{
public partial class YourDataService
{
// will hold the impersonation context between sending and receiving
private System.Security.Principal.WindowsImpersonationContext _impersonationContext;

// set up impersonation ...
partial void YourData_SendingRequest(ODataSendingState state)
{
var httpIdentity = HttpContext.Current.User.Identity;

System.Security.Principal.WindowsIdentity identity;
if (HttpContext.Current.User.Identity.GetType().Name == "GenericIdentity")
{
identity = System.Security.Principal.WindowsIdentity.GetCurrent();
}
else
{
identity = (System.Security.Principal.WindowsIdentity) HttpContext.Current.User.Identity;
}

state.Request.PreAuthenticate = true;
state.Request.Credentials = System.Net.CredentialCache.DefaultCredentials;

_impersonationContext = identity.Impersonate();
}

// ... and revert after completion of request
partial void YourData_ReceivedResponse(ODataReceivedState state)
{
if (null == _impersonationContext)
{
return;
}
_impersonationContext.Undo();
}
}
}

With this and having your AppPool run under a specific service account (with some privileges that allow impersonation, which is not the case for the default AppPoolIdentity), your wrapped ODATA service will be using the same credentials as your client who called your LightSwitch wrapper and the rest of your application.

From your now freshly impersonated ODATA controller you can call back into your LightSwitch ServerApplicationContext via CreateContext()and use the intrinsic permission model and all the other goodness LightSwitch offers us.

For more information on Impersonation and ODATA Controller see [HOWTO] Impersonate Calls to OData Service Reference.

Note1: be sure to run the AppPool under a service account user with appropriate permissions.
Note2: in case you are testing inside Visual Studio with IISExpress, you might notice that the impersonated user in the ODATA controller is emtpy and that the full request returns IsAuthenticated == false. You will have to enable Windows Authentication (or any other authentication) in IIS to make it work. Inside the \My Documents\IISExpress\config\applicationhost.config check for the setting of the windowsAuthentication entry. For more information see the Stack Overflow answer IIS Express Windows Authentication.

Comments

  1. Jean Pierre Chauny says:

    Very cool!!!
    Congratulations! This opens lots of possibilities for Lightswitch. It’s a pitty that the Lightswitch team has abandoned (I am speculating) the project.
    A Question:
    If you have a sql server database that is secured with integrated security and you connect to it from Lightswitch using Windows Authentication, how do you pass through the user credentials down to the database layer?

    • Ronald Rink says:

      Actually we run the web application (with its ODATA controllers) not under default AppPool identity, but under a named service account. So when impersonating from LightSwitch against the ODATA controllers we use make the call against the ODATA controller as the logged in LightSwitch user, but the actual SQL connection to the underlying DB is still done via the named user that is defined in the AppPool of the ODATA web app. Maybe I am getting something wrong, but in my opinion the actual end user / client (in LightSwitch) should not make the call to the underlying DB with his account at all. Is this something you need in your application?
      And yes: a pity regarding LigthSwitch’s stalled development. We now switched to standard ASP.NET MVC (for our new projects) which gives a us much more possibilities.
      Regards, Ronald

      • Jean Pierre Chauny says:

        I didn’t formulate my question right, sorry for that …
        I meant, if you connect to an existing SQL database (not the intrinsic one) as an external Datasource (analogous to this post’s example of connecting to an external oData Datasource but this time a SQL Datasorce) and the mentioned database is secured with integrated security and you connect using Windows authentication. This database could represent an external legacy system that does not expose an oData middle tier. When you connect the standard way, the identity that passes through to the database is also this annoying AppPool Identity.

      • Ronald Rink says:

        I never tried this with a direct SQL connection. But setting the AppPool Identity to a real named user/service account might be a viable approach (but as I said, I have not tested that).

  2. Jean Pierre Chauny says:

    Dear Ronald:
    Check this out about expanding your Lightswitch application with MVC and Web Api.
    http://blog.ofanitguy.com/2013/10/08/how-to-create-an-expanded-lightswitch-2013-project/
    MVC is a nice and popular framework but the development productivity in Lightswitch is much higher. Hopefully Microsoft will reconsider reactivating the project and perhaps enabling a new Dektop client in WPF.
    The new edge browser in Windiws 10 does not support Silverlight, these are bad news for Lightswitch.

    • Ronald Rink says:

      true, with LS you gain visible results much faster. However, as soon as you want to start with customisations or adjustments for entity relationship types that are not supported by LS (such as 0..1:1) I experienced much more problems than with MVC.

      • Jean Pierre Chauny says:

        In my opinion the biggest disadvantages of Lightswitch is the lack of a code first approach like in entity framework when modeling your domain entities.
        According to Domain Driven Design Principle you need 3 kind of artifacts:
        Entities, Value Objects and Services. There s not good support for Value Objects in Lightswitch under other disadvantages.

  3. I’m glad your wrote this article. I was looking for an answer to my problem for ages. Unfortunately, after putting you code into my LS V3 project (the one the user logs into and tries to call another LS V2 OData Service), redeploying it and setting it’s application pool’s identity as “NetworkService” which according to my IIS production server’s “User Rights Assignment” in the “Local Security Policies” MMC has the rights to “Impersonate a client after authentication”. I’ve also enabled impersonation within the app’s web.config.

    What else do I need to do to make the impersonation work? Of course debugging (running the project within visual studio) everything works fine.

    Is it because my target OData service was built using Lightswitch V2? Does that service require the same modifications as above to make impersonation work?

    Btw, both LS apps have been configured to use Windows Authentication.

    I’ve been pulling my hair over this for the longest time.

    I really do hope you can help me with this. I cannot find any other post/article in the Internet addressing this problem.

  4. I’m glad your wrote this article. I was looking for an answer to this problem for ages. Unfortunately, after putting your code into my LS V3 project (the one the user logs into and tries to call another LS V2 OData Service), redeploying it and setting it’s application pool’s identity as “NetworkService” which according to my IIS production server’s “User Rights Assignment” in the “Local Security Policies” MMC has the rights to “Impersonate a client after authentication”, impersonation still failed to work. I’ve also enabled impersonation within the app’s web.config.

    What else do I need to do to make the impersonation work? Of course debugging (running the project within visual studio) everything works fine.

    Is it because my target OData service was built using Lightswitch V2? Does that service require the same modifications as above to make impersonation work?

    Btw, both LS apps have been configured to use Windows Authentication.

    I’ve been pulling my hair over this for the longest time.

    I really do hope you can help me with this. I cannot find any other post/article in the Internet addressing this problem.

    P/s: I apologize if this post appears twice on your page. I wasn’t sure it got through the first time (it didn’t immediately appear on your page). So, I decide to use another browser and try again. :D

    • Ronald Rink says:

      Hi, the more comments – the better ; -) I am not aware that it would not work with LS2 services.
      Where do your LS2 services run? on a different server? Can you trace the calls (e.g. with Fiddler) to see, if it is tried to do the authentication with the authenticated user?

      We had this running only with regular ODATAv3 services and had no problems until now. However as we nearly migrated everything away from LS (due to the retirement of the product) we only have one application left that uses this scenario.

      Maybe you can implement some diagnostics logging to see if the event handler has been called in release and which identity you are trying to use there.

      Regards, Ronald

      • Thank you very much for your response.

        The LSv2 services are running on the same server that I deployed the LSv3 app that’s trying to use it. Using other Windows Apps like LINQPad or Excel (on my development machine) to access the LSv2 services, there’s no problems with authentication and it works as expected.

        Here’s a the error message I got off the LSv3 app’s trace.axd. I don’t know what to make of it. Of course the “servername.domain” is not the actual server name. Had to replace the actual name with that here for safety reasons (that server is actually accessible from the Internet). I don’t understand why the consuming app (the LSv3 app) just couldn’t use the the service when deployed on the same server BUT successfully accesses the same service via the Internet when it’s running in debug/release mode via Visual Studio on my dev machine from home.

        [Microsoft.LightSwitch.DataService][Application:Error][LightSwitchServiceApplication.RnDSv1Data:ODataProviderQuery] An exception occurred issuing the following request: servername.domain/…/refLGMStaffs()$count?$filter=status%20ne%20null%20and%20status%20eq%20true%20and%20substringof(‘sokongan’,category)%20eq%20false

        Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.Data.Services.Client.DataServiceQueryException: An error occurred while processing this request. —> System.Data.Services.Client.DataServiceClientException: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/…/strict.dtd"&gt;

        Not Found

        HTTP Error 404. The requested resource is not found.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: