Continuing from the previous article Securing Dynamic Data Preview 4 Refresh – Part 1 we will proceed to complete the second two items in the to do list below:
Things we will need to Do
- Dynamic Data Route Handler
- Remove Delete Link from List and Details pages
- Secure Meta model classes
- Make columns read only using Entity Templates.
Secure Meta model classes
It would have been nice if I could have overridden the Scaffold and IsReadOnly methods of the MetaColumn class for this there would have been less code peppered around Dynamic Data but we can hope for the future.
public class SecureMetaModel : MetaModel { /// <summary> /// Creates the metatable. /// </summary> /// <param name="provider">The metatable provider.</param> /// <returns></returns> protected override MetaTable CreateTable(TableProvider provider) { return new SecureMetaTable(this, provider); } }
Listing 1 – SecureMetaModel class
public class SecureMetaTable : MetaTable { /// <summary> /// Initializes a new instance of the <see cref="SecureMetaTable"/> class. /// </summary> /// <param name="metaModel">The meta model.</param> /// <param name="tableProvider">The table provider.</param> public SecureMetaTable( MetaModel metaModel, TableProvider tableProvider) : base(metaModel, tableProvider) { } protected override void Initialize() { base.Initialize(); } /// <summary> /// Gets the scaffold columns. /// </summary> /// <param name="mode">The mode.</param> /// <param name="containerType">Type of the container.</param> /// <returns></returns> public override IEnumerable<MetaColumn> GetScaffoldColumns( DataBoundControlMode mode, ContainerType containerType) { return from column in base.GetScaffoldColumns(mode, containerType) where column.SecureColumnVisible() select column; } }
Listing 2 – SecureMetaTable class
/// <summary> /// Secures the column visible. /// </summary> /// <param name="column">The column.</param> /// <returns></returns> public static Boolean SecureColumnVisible(this MetaColumn column) { var userRoles = Roles.GetRolesForUser(); var activeDenys = column.GetColumnPermissions(userRoles); if (activeDenys.Contains(ColumnDeny.Read)) return false; else return true; }
Listing 3 – SecureColumnVisible extension method
[Flags] public enum ColumnDeny { Read = 1, Write = 2, }
Listing 4 – ColumnDeny enum
So in the above four listings we have the simplified solution to hide columns based on user roles.
How it works
Listing 1 is required to let us include the SecureMetaTable in the default model Listing 2 is the SecureMetaTable all we do here is filter the columns based on user roles see Listing 3 SecureColumnVisible which hides columns based on ColumnDeny.Read this very easily and cleanly done thanks to the ASP.Net team and letting us derive from the meta classes.
Make columns read only using Entity Templates
Now to make columns read only in Edit or Insert modes based on use roles, for this we will modify tow of the default EntityTemplates Default_Edit.ascx.cs and Default_Insert.ascx.cs in these template we add the code in listing 5 to the DynamicControl_Init event handler.
if (currentColumn.SecureColumnReadOnly()) dynamicControl.Mode = DataBoundControlMode.ReadOnly; else dynamicControl.Mode = DataBoundControlMode.Edit; //Insert for the insert templateListing 5 – Code to make a field read only in Edit or Insert modes
protected void DynamicControl_Init(object sender, EventArgs e) { DynamicControl dynamicControl = (DynamicControl)sender; dynamicControl.DataField = currentColumn.Name; if (currentColumn.SecureColumnReadOnly()) dynamicControl.Mode = DataBoundControlMode.ReadOnly; else dynamicControl.Mode = DataBoundControlMode.Edit; //Insert for the insert template }
Listing 6 – finished DynamicControl_Init for the Edit Entity Template
/// <summary> /// Secures the column read only. /// </summary> /// <param name="column">The column.</param> /// <returns></returns> public static Boolean SecureColumnReadOnly(this MetaColumn column) { var userRoles = Roles.GetRolesForUser(); var activeDenys = column.GetColumnPermissions(userRoles); if (activeDenys.Contains(ColumnDeny.Write)) return true; else return false; }
Listing 7 – SecureColumnReadOnly extension method
/// <summary> /// Get a list of permissions for the specified role /// </summary> /// <param name="attributes"> /// Is a AttributeCollection taken /// from the column of a MetaTable /// </param> /// <param name="role"> /// name of the role to be matched with /// </param> /// <returns>A List of permissions</returns> public static List<ColumnDeny> GetColumnPermissions(this MetaColumn column, String[] roles) { var permissions = new List<ColumnDeny>(); // you could put: // var attributes = column.Attributes; // but to make it clear what type we are using: System.ComponentModel.AttributeCollection attributes = column.Attributes; // check to see if any roles passed if (roles.Count() > 0) { // using Linq to Object to get // the permissions foreach role permissions = (from a in attributes.OfType<SecureColumnAttribute>() where a.HasAnyRole(roles) select a.Permission).ToList(); } return permissions; }
Listing 8 – GetColumnPermissions extension method
The above code Listings 5 & 6 simply test to see if the column is restricted to read only based on users roles and uses Listing 7 & 8 extension methods to achieve this.
And finally to make this work with Dynamic Data we need to modify the Global.asax
public class Global : System.Web.HttpApplication { private static MetaModel s_defaultModel = new SecureMetaModel(); public static MetaModel DefaultModel { get { return s_defaultModel; } } public static void RegisterRoutes(RouteCollection routes) { DefaultModel.RegisterContext( typeof(Models.NorthwindEntities), new ContextConfiguration() { ScaffoldAllTables = true }); routes.Add(new DynamicDataRoute("{table}/{action}.aspx") { Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }), RouteHandler = new SecureDynamicDataRouteHandler(), Model = DefaultModel }); } void Application_Start(object sender, EventArgs e) { RegisterRoutes(RouteTable.Routes); } }
Listing 9 – adding the SecureMetaModel to the Globalasax
Once we have a declared our metamodel property in the Global.asax we can just reference it for the app by using it to register the context.
Download
I’m working on better hyperlinks for another post to follow shortly, these hyperlinks will offer the option of:
- Hiding the link if it is disabled
- Showing plain text if disabled
via an property at design time.
4 comments:
Hi Steve, Just wondering if you have a good method of mixing your overrides of the MetaModel/MetaTable. I was already using the SecureMetaModel and then I wanted to use the HideColumnIn attribute which requires another MetaModel/MetaTable override. Seemed like I might be able to do a union of the two, but I just wondered if you had any suggestions.
Thanks and Cheers, Mike
Hi Michael, have a look at http://csharpbits.notaclue.net/2010/06/securing-dynamic-data-4-replay.html
I believe if you mix that with
http://csharpbits.notaclue.net/2010/02/new-way-to-do-column-generation-in.html
you should have what you want.
Thanks Steve, I'll give it a try.
Hi Steve,
I've been using secure meta model for a while, but i had to add another model to DD and used EFCodeFirstDataModelProvider. My my registration:
var model1 = new MetaModel();
model1.RegisterContext(new EFCodeFirstDataModelProvider(() => new Models.RVS_logistikaEntities()),
new ContextConfiguration() { ScaffoldAllTables = true });
model1.RegisterContext(new EFCodeFirstDataModelProvider(() => new Models.NoringeEntities()),
new ContextConfiguration() { ScaffoldAllTables = true });
model1.RegisterContext(new EFCodeFirstDataModelProvider(() => new Models.PasaraiGamybaEntities()),
new ContextConfiguration() { ScaffoldAllTables = true });
everything works but secure meta model isn't working. any clues?
Post a Comment