Making a Lightswitch intrinsic ria service row level security aware (part 1 of 2)

Introduction

In the past I have done several attempts to find a solution for the fact that an intrinsic Ria Service attached to a LightSwitch project simply disregards all row level security rules from the corresponding LightSwitch project.

In this article I’m proposing a very light and pragmatic approach for making a Ria Service row level security aware.

This article elaborates further on my previous article: Getting rid of compilation warnings…

An accompanying sample for this article can be found here : http://code.msdn.microsoft.com/vstudio/Setting-up-a-Ria-Service-6f7950b0

Why does an intrinsic Ria Service disregards the row level security?

The reason why a Ria service has no knowledge about the row level security rules in the various _Filter() methods, is simply because the Ria Service connects directly to the (intrinsic) database.

You can see this In the connection string used in the various domain service classes. I tend to isolate this piece of code in a base class called LightSwitchDomainServiceBase:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.DomainServices.Server;
using LightSwitchApplication.Implementation;
using System.Data.EntityClient;


namespace RiaService
{
     public class LightSwitchDomainServiceBase : DomainService
     {
         #region Database connection
         private ApplicationData m_context;
         public ApplicationData Context
         {
             get
             {
                 if (this.m_context == null)
                 {
                     string connString =
                         System.Web.Configuration.WebConfigurationManager
                         .ConnectionStrings[“_IntrinsicData”].ConnectionString;
                     EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder();
                     builder.Metadata =
                         “res://*/ApplicationData.csdl|res://*/ApplicationData.ssdl|res://*/ApplicationData.msl”;
                     builder.Provider =
                         “System.Data.SqlClient”;
                     builder.ProviderConnectionString = connString;
                     this.m_context = new ApplicationData(builder.ConnectionString);
                 }
                 return this.m_context;
             }
         }
         #endregion
         protected override int Count<T>(IQueryable<T> query)
         {
             return query.Count();
         }
     }
}

 

An example row level security rule

Imagine we have Customer table and following row level security rule:

using System;
using System.Linq.Expressions;


namespace LightSwitchApplication
{
     public partial class ApplicationDataService
     {


        partial void Customers_Filter(ref Expression<Func<Customer, bool>> filter)
         {
             filter = e => e.LastName.EndsWith(“1″);
         }
     }
}

So, my row level security rules tells me that a user can only see customers who’s last name end with “1”. This is a simplified example, but you can replace this simple rule with code having any level of complexity. But that’s for making my point here not important.

Isolating the LightSwitch application’s row level security rules in a dedicated static class.

So, add to the LightSwitch server project a new class called RowLevelSecurityFilters.cs:

using System;
using System.Linq.Expressions;
using LightSwitchApplication.Implementation;
namespace LightSwitchApplication
{
     internal static class RowLevelSecurityFilters
     {
         public static  Expression<Func<Customer, bool>> CustomerFilter()
         {
             return e => e.LastName.EndsWith(“1″);
         }
     }
}

Make sure to make the class internal and not public.

Consuming the above filter in our regular _Filter method is very straightforward:

using System;
using System.Linq.Expressions;


namespace LightSwitchApplication
{
     public partial class ApplicationDataService
     {


        partial void Customers_Filter(ref Expression<Func<Customer, bool>> filter)
         {
             filter = RowLevelSecurityFilters.CustomerFilter();
         }
     }
}

This was easy, right ?

How to do inject this filter in our Ria Service?

Step 1: file share the RowLevelSecurityFilters class from the LightSwitch Server project with the Ria Service project.

sharefilterclass

 

If you don’t know how the add a file as a link, see previous article

Step 2: consume the RowLevelSecurity filter in the domain service:

namespace RiaService.Services
{
     using LightSwitchApplication;
     using RiaService.DTO;
     using System.Linq;
     using System.ServiceModel.DomainServices.Server;


    public class ExampleCustomerDomainService : LightSwitchDomainServiceBase
     {
         [Query(IsDefault = true)]
         public IQueryable<ExampleCustomerDTO> GetCustomersDTO()
         {
            var filter = RowLevelSecurityFilters.CustomerFilter();


            return from c in this.Context.Customers.Where(filter)
                    select new ExampleCustomerDTO
                    {
                        Id = c.Id,
                        CustomerName = c.LastName,
                    };
         }
     }
}

 

 

As you can see, it’s still the responsibility of the developer to inject the row level security rule, but it’s good that you can decide whether you want to use the row level security rule or not. Indeed, there are occasions where you want deliberately to bypass these rules !

 

Dude, tell me how many “Customer” classes are around in LightSwitch?

The reason why the above functions correctly, seems at first glance more obvious than it is.

The tricky point is that our Customer class in the LightSwitch dll is not at all the same as the customer class in our Ria Service.

LightSwitch has the notion of data layer entity types and business entity types. With our Customers as example, in the LightSwitch Server project, the customer looks as follows:

CustomerInLightSwitch

 

LightSwitchApplication.Customer is the “rich” business type, whereas in our Ria Service we have no access to this rich business type but only to the “data layer” entity type:

CustomerInRia

The type, what I call the “data layer” type is : LightSwitchApplication.Implementation.Customer.

How come the RowLevelFilter can handle both LightSwitchApplication.Customer and LightSwitchApplication.Implementation.Customer?

Well, the RowLevelSecurityFilters class has a using statement to LightSwitchApplication.Implementation, but depending on which project is consuming this class, this using will be “used” or not.

 

using System;
using System.Linq.Expressions;
using LightSwitchApplication.Implementation;
namespace LightSwitchApplication
{
    internal static class RowLevelSecurityFilters
    {
        public static  Expression<Func<Customer, bool>> CustomerFilter()
        {
            return e => e.LastName.EndsWith(“1″);
        }
    }
}

When the LightSwitch server project consumes the RowLevelSecurityFilters, the Customer class will be first resolved to LightSwitchApplication.Customer, because it’s in the namespace LightSwitchApplication.

When the Ria service consumes the RowLevelSecurityFilters class, the Customer class be resolved using the LightSwitchApplication.Implementation namespace.

Is this approach generating compilation warnings?

NO.

Recall my previous post dedicated to avoiding compilation warnings when using the “file share” method.

The difference with sharing the ApplicationDataObjectContext class is that our class is our own class, whereas ApplicationDataObjectContext is a code generated class.  That means that we have full control on the access modifier of the class, so we took the “internal” access modifier. By doing so, the class is not exposed to the “external world” and will not give the compilation warning.

No magic involved and no string attached, just some very basic OO (object orientation), which goes hand in hand with LightSwitch.

Cheers.