Sample: Security Manager - ASP.NET Style
Yesterday I posted about a class called SecurityManager, which had the job of checking role-based security settings and exposing properties that could be data bound to. In that example, I created a custom BindingSource in Windows Forms so that I could bind the Visible and Enabled properties of controls to the class, all through the designer.
The same functionality can be achieved in ASP.NET. Let’s first create our SecurityManager class again:
public class SecurityManager
{
private static readonly SecurityManager _current = new SecurityManager();
public static SecurityManager Current
{
get { return _current; }
}
public bool CanSeeAdminPanel
{
// Change this code to a role that does/doesn't exist
get { return Thread.CurrentPrincipal.IsInRole("Administrators"); }
}
}
Declaratively binding the properties of our panel in ASP.NET is quite easy. Our page could contain:
<asp:Panel
ID="adminPanel"
runat="server"
Visible="<%# ExampleApplication.SecurityManager.Current.IsAdministrator %>"
>
This is only visible to admins
</asp:Panel>
This is a nice approach, as it sticks to our “pull” based model in that we don’t need to reference the UI control directly to set its properties. The only line of code needed in our code behind is:
this.DataBind();
If you run the code above, it will work perfectly. However, there is a drawback to this approach: we don’t get any designer support.
Introducing ExpressionBuilders
Instead of using the ASP.NET data binding infrastructure, I’m going to make use of ASP.NET ExpressionBuilders. You may be familiar with accessing localization resources in ASP.NET, where you use a custom expression prefixed with “Resources:“. Under the hood, ASP.NET takes that expression and converts it into code at compile time using the CodeDom. Accessing a resource might look like this:
<asp:Label Runat="server" Text="<%$ Resources: GlobalStrings, WelcomeMessage %>" />
What you may not know is that we can create our own ExpressionBuilders. All we need to do is inherit from the ExpressionBuilder base class, do a little CodeDom manipulation, and add an entry to our web.config file to make ASP.NET aware of our custom expression builder. There are plenty of articles on the web about how to do custom ExpressionBuilders - the best one I’ve found is Eilon Lipton, which guides you through creating the designer components as well.
Following Eilon’s tutorial, I created my custom ExpressionBuilder, ExpressionEditor, and ExpressionEditorSheet:
[ExpressionPrefix("Security")]
[ExpressionEditor(typeof(SecurityExpressionEditor))]
public class SecurityExpressionBuilder : ExpressionBuilder
{
public override CodeExpression GetCodeExpression(
BoundPropertyEntry entry,
object parsedData,
ExpressionBuilderContext context)
{
Type targetPropertyType = entry.PropertyInfo.PropertyType;
return new CodePropertyReferenceExpression(
new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression(
typeof(SecurityManager)), "Current"),
entry.Expression);
}
}
/// <summary>
/// The editor we'll use to edit the expression.
/// </summary>
public class SecurityExpressionEditor : ExpressionEditor
{
public override object EvaluateExpression(
string expression,
object parseTimeData,
Type propertyType,
IServiceProvider serviceProvider)
{
return false;
}
public override ExpressionEditorSheet GetExpressionEditorSheet(
string expression,
IServiceProvider serviceProvider)
{
return new SecurityExpressionEditorSheet(
expression,
serviceProvider);
}
}
/// <summary>
/// Editor object that will appear in the PropertyGrid of the designer.
/// </summary>
public class SecurityExpressionEditorSheet : ExpressionEditorSheet
{
private string _securityConstraint;
public SecurityExpressionEditorSheet(
string expression,
IServiceProvider provider)
: base(provider)
{
_securityConstraint = expression;
}
[TypeConverter(typeof(SecurityListConverter))]
public string SecurityConstraint
{
get { return _securityConstraint ?? string.Empty; }
set { _securityConstraint = value; }
}
public override string GetExpression()
{
return this.SecurityConstraint;
}
}
/// <summary>
/// TypeConverter to provide a list of security property names.
/// </summary>
public class SecurityListConverter : StringConverter
{
public override bool GetStandardValuesExclusive(
ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesSupported(
ITypeDescriptorContext context)
{
return true;
}
public override TypeConverter.StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
List<string> properties = new List<string>();
foreach (PropertyInfo property in typeof(SecurityManager).GetProperties(
BindingFlags.DeclaredOnly | BindingFlags.Public
| BindingFlags.Instance))
{
properties.Add(property.Name);
}
return new StandardValuesCollection(properties.ToArray());
}
}
Finally, all I had to do was add the entry to my web.config:
<compilation debug="true">
<expressionBuilders>
<add
expressionPrefix="Security"
type="ExampleApplication.SecurityExpressionBuilder"
/>
</expressionBuilders>
</compilation>
If I now flip into the designer, I can create my expression:
The markup that the designer generates would look something like this:
<asp:Panel ID="adminPanel" runat="server" Visible="<%$ Security:IsAdministrator %>"> This is only visible to admins </asp:Panel>
And the code at runtime, thanks to the CodeDom injection, would look like this:
@__ctrl.Visible = ((bool)(WebApplication2.SecurityManager
.Current.IsAdministrator));
ASP.NET ExpressionBuilders are a very neat feature and one that I’d never looked at before. I can imagine doing all kinds of features in with custom expression builders - from security, pointing to static/global variables, and the next thing on my list: hooking into the ASP.NET data binding infrastructure to add the concept of DataContexts like WPF, custom binding expressions, converters and removing the need for controls like the ObjectDataSource and FormView
Filed under: Binding


Great article Paul
Nice one Stovie. I had never actually done an expression builder before.
Today’s Links
[…] With this in mind I went on a quest to find similar sites […]
Paul,
Great article - could I use this for a web part ?
If I have a portal app which loads in web parts, how could I get the web part to pick up the security settings and only display those web parts that the user should see, depending on his security role.
I am guessing it would be something to do with the property attributes with the web part…?
Grateful for any help.
Paul,
One further (possibly stupid) question is this really two way databinding - is it possible to retrieve the value as well as setting it ?
Awesome. The best part is how you create a GOOD example following best practices. Thanks!