OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Role Based Silverlight Behaviors

Well working on a few WCF RIA Services projects a need for the UI to update based on what roles the current user belongs to came up with each project. To make this easier for all of us working on these projects I created some behaviors that accepts a list of roles and updates the UI accordingly. I decided to include this in an open source library that is available at the associated OpenLight Group Common CodePlex project site. In this article we will discuss the base class used for all of the role based behaviors and look at the RoleBasedEnabledBehavior as an example of how to implement the base class.

The first class we will look at is the abstract BaseRoleBasedBehavior class. This class is takes a generic parameter that is constrained to DependencyObject to keep it consistent with Behavior<T> base class.

public abstract class BaseRoleBasedBehavior<T>
        : Behavior<T> where T : DependencyObject

 

 

BaseRoleBaseBehavior includes a property to hold the list of role names:

 

[Category("Security Settings")]

[Description("Associated control will only be visible if the current user is a member of one or more of these roles.")]
public List<string> AllowedRoles { get; set; }

 

The constructor handles initializing the list of roles and wiring handlers to the LoggedIn and LoggedOut events of the current webcontext:

 

public BaseRoleBasedBehavior()

{
	this.AllowedRoles = new List<string>();
	if (DesignTimeHelper.IsRuntime)
	{
		WebContextBase.Current.Authentication.LoggedIn += 
			new System.EventHandler<AuthenticationEventArgs>(UpdateAssociatedObject);
		WebContextBase.Current.Authentication.LoggedOut += 
			new System.EventHandler<AuthenticationEventArgs>(UpdateAssociatedObject);
	}

}

 

There are a few items to note in this method.

  1. The call to DesignTimeHelper.IsRuntime – this is a static class also included in the common library that is used to allow for a good design time experience. As in this example, wiring handlers to the current WebContext at design time would throw an exception.
  2. The use of WebContextBase – the prevents the need to include the RIA services web project that will generate the concrete WebContext class.
  3. Marking the UpdateAssociatedObject method as abstract allows each inheriting behavior to handle the authentication events as needed.
protected abstract void UpdateAssociatedObject(object sender, AuthenticationEventArgs e);

 

Another example of using the DesignTime helper is when checking the current user’s membership to a role. In this case, the helper prevents the designer from calling a method on the User object that would be null at design time. This method is included to simplify checks for all of the behaviors that will inherit from this base class.

protected bool IsInRole(string roleName)
{
	if (DesignTimeHelper.IsRuntime)
	{
		return WebContextBase.Current.Authentication.User.IsInRole(roleName);
	}
	else
	{
		return true;
	}
}

As with all behaviors, the OnAttached and OnDetaching methods are implemented:

protected override void OnAttached()
{
	base.OnAttached();
	this.UpdateAssociatedObject(this, null);
}
protected override void OnDetaching()
{
	base.OnDetaching();
}

Notice that the UpdateAssociatedObject is called from the OnAttached method to ensure that the UI is updated when the behavior is applied as well as when the user logs in or out. With that our base class is completed. Now to implement a role based behavior we simply need to inherit from this class and implement the UpdateAssociatedObject method.

Here is an example of a behavior that disables a control if the current user is not in one of the allowed roles:

public class RoleBasedEnabledBehavior
	 : BaseRoleBasedBehavior<Control> 
{
	protected override void UpdateAssociatedObject(object sender, AuthenticationEventArgs e)
	{
		this.AssociatedObject.IsEnabled =
			(this.AllowedRoles.Where(r => IsInRole(r)).Count() > 0);
	}

}

 

By using the Control type for the generic parameter we gain access to the enabled property of the AssociatedObject property. All that is left to do is check to see if the user is a member of any of the roles specified in the AllowedRoles property of the base class. By using LINQ and the IsInRole method of the base class, this becomes a single line of code to implement the functionality of this behavior.

It is now simple to disable a control based on the current user’s role membership, like so:

<Button Content="Button">
	<i:Interaction.Behaviors>
		<behaviors:RoleBasedEnabledBehavior>
			<behaviors:RoleBasedEnabledBehavior.AllowedRoles>
				<System:String>string</System:String>
			</behaviors:RoleBasedEnabledBehavior.AllowedRoles>
		</behaviors:RoleBasedEnabledBehavior>
	</i:Interaction.Behaviors>
</Button>

Note: Make sure that the interactivity, system and behaviors namespaces are imported:

 

xmlns:behaviors="clr-namespace:OpenLightGroup.Common.Behaviors;assembly=OpenLightGroup.Common"

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:System="clr-namespace:System;assembly=mscorlib"

 

Again both the base class and the implementing behavior are available in the OpenLight Common Library here: http://olgcommon.codeplex.com/

Hope this sparks some innovation… or at the very least helps shave off some time spent coding for someone ;)





Comments are closed.