J King
J King

Reputation: 4434

Wpf User Security Strategy

BACKGROUND:

I am creating a WPF application (.NET 4.5 with MVVM-Light)

I have created user roles in the database that backs the WPF application where the users of the WPF App have an assigned role (i.e. user, manager, owner, admin)

WHAT I WANT:

My client wants to be able to restrict what the users see and what the users can do based on their role. There are some views that will be seen by all users, therfore some visual elements (grids, buttons etc..) should be hidden or disabled depending on the users role.

WHAT I HAVE:

I have created an IUserService that gets injected into every viewmodel. The roles that I have created have a field that marks their security level(simply an integer 1 through 5). I want to be able to restrict the visiblity of visual elements based on this number.

For example, My plan is to bind the visibility of the element to a boolean property (using a boolToVisibility Converter) in the viewmodel (Level1, Level2, etc) and that property would return true if the users level matches or is greater than the property level.

MY CONCERNS:

My concern is that this is a lot of work to implement in every viewmodel and on every visual element that is needed. Also, I already have some visual elements that are affected by other business logic.

QUESTION:

What is an efficient way to restrict users ability to "view" visual elements based on a user role strategy?

I am prepared to start this work, But I would love to hear some other ideas from the community on how user role based security is implemented in a WPF application.

Upvotes: 2

Views: 3295

Answers (2)

Omri Btian
Omri Btian

Reputation: 6547

You can create an attached property to determine each control's access level

public class VisibilitySecurityLevel 
{
    public static readonly DependencyProperty SecurityLevelProperty = 
        DependencyProperty.RegisterAttached("SecurityLevel", typeof(int), typeof(FrameworkElement), new PropertyMetadata(5));

    public static void SetSecurityLevel(UIElement element, int value)
    {
        element.SetValue(SecurityLevelProperty, value);
    }
    public static int GetSecurityLevel(UIElement element)
    {
        return (int)element.GetValue(SecurityLevelProperty);
    }
}

Apply it to the control's you want to be managed by user role

    <Button local:VisibilitySecurityLevel.SecurityLevel="3"/>
    <CheckBox local:VisibilitySecurityLevel.SecurityLevel="2"/>

use a base implicit style that will bind the visibilty of the controls based on thier security level using a converter

       <local:AccessLevelToVisibilityConverter x:Key="AccessLevelToVisibilityConverter"/>
       <Style TargetType="{x:Type FrameworkElement}">
            <Setter Property="Visibility">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource AccessLevelToVisibilityConverter}">
                        <Binding Path="UserRole"/>
                        <Binding RelativeSource="{RelativeSource Mode=Self}"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>

The converter:

public class AccessLevelToVisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        int userRole = (int)values[0];
        int controlAccessLevel = (int)(values[1] as FrameworkElement).GetValue(VisibilitySecurityLevel.SecurityLevelProperty);

        return (userRole <= controlAccessLevel) ? Visibility.Visible : Visibility.Hidden;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Note that in my implementation the lowest user role 5 is the most restricted one (can only see controls with access level of 5) and user role 1 is the most privileged (can see controls with access levels 1 -5). This is why the default access level is 5 (new PropertyMetadata(5)) and The converter is binded to an integer UserRole property in the viewModel indicating the user's access priviliages (1 - 5)

Hope this helps

Upvotes: 6

Taras
Taras

Reputation: 1128

According to my comment, here is converter:

class SecurityLevelToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(int.Parse((string)value) < int.Parse((string)parameter)) ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

And here is an example of how you can use it in XAML:

<Button Style="{StaticResource MyButtonStyle}" 
        DataContext="{DynamicResource System.CurLevel}" 
        Visibility="{Binding Path=Value, Converter={StaticResource SecurityLevelToVisibilityConverter}, ConverterParameter=3}"/>

"ConverterParameter=3" means that users with security level of "3" can see the button.

Upvotes: 2

Related Questions