Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF UI Permissions, which are defined in database

Till now I've used very simple User-Role version and controlled permissions per Visibility / IsEnabled / Command.CanExecute with converters:

 <TabItem Visibility="{Binding UserRole, Converter={StaticResource PackerVisible}}" >...

My converter looks like:

 <conv:UserVisibilityConverter x:Key="PackerVisible" Allow="Packer,Lagerleiter,Entwickler" />

It's relative easy if you have not much roles, but even so you have no good overview: who will get access to this or other UI elements.

One more bad thing is that UserRoles (stored in DB) used here as strings - Allow="Role1, Role2, Role3".

And in my new project I have more roles/permissions, what makes this solution more ugly.

I've searched and found nothing how to implement it much better. A bit better is maybe to use enum for role names in code, but how to synchronise enum with database?

Other solutions i've found (solution1, solution2) are better, because they don't need converters, but permissions are set again as strings in XAML:

// AuthToVisibility
<Image ... Visibility="{op:AuthToVisibility "CanView"}" />

// AutoToEnabled
<MenuItem IsEnabled="{op:AuthToEnabled "CanClose"}">...</MenuItem>

What is the best way to handle permissions stored in the database towards UI? Is there any pattern?

like image 866
Shwed_Berlin Avatar asked Dec 21 '25 01:12

Shwed_Berlin


1 Answers

As long as nobody answers, I will try. I have a better (not ideal) solution.
First implementation, below I will write some words about it.

  1. Permission Class:

    public class Permission
    {
        public bool IsAllowed { get; set; }
        internal List<string> AccessControllList { get; private set; }
    
        internal Permission(params string[] allowedRoles)
        {
            IsAllowed = false;
            AccessControllList = new List<string>();
            foreach (var role in allowedRoles)
            {
                AccessControllList.Add(role);
            }
        }
    }
    
    • main property - IsAllowed
    • internal list of role names AccessControlList which are allowed to run/see some function/block
    • internal constructor to fill ACL.
      Small and simple.
  2. PermissionController:

    public class PermissionController : INotifyPropertyChanged
    {
        #region Singleton
        ...
        //constructor
        private PermissionController()
        {
            Initialize();
            UserModel.Instance.LoginChanged += new LoginChangedEventHandler(UpdatePermissions);
        }
        #endregion
    
        //role names from database as string const
        private const string PACKER = "Packer";
        private const string CHIEF = "Lagerleiter";
        private const string QSWORKER = "QSMitarbeiter";
        private const string DEVELOPER = "Entwickler";
    
        PropertyInfo[] _localProperties;
    
        //Permissions defined as properties
        public Permission WEstart { get; private set; }
        public Permission WEreset { get; private set; }
        public Permission WEclose { get; private set; }
    
        public Permission Manage { get; private set; }
        public Permission TestPhase { get; private set; }
    
        private void Initialize()
        {
            _localProperties = typeof(PermissionController).GetProperties();
    
            WEwork          = new Permission(PACKER, CHIEF);
            WEreset         = new Permission(CHIEF);
            WEclose         = new Permission(PACKER, CHIEF);           
            Manage          = new Permission(CHIEF);
            TestPhase       = new Permission(DEVELOPER);
        }
    
        private void UpdatePermissions(object sender, EventArgs e)
        {            
            var currentUser = (UserModel)sender;
    
            //if user is logged out -> return
            if (!currentUser.IsLoggedIn)
                return;
    
            var permissionType = typeof(Permission);
            foreach (PropertyInfo property in _localProperties)
            {
                if (property.PropertyType != permissionType)
                    continue;
    
                var permission = (Permission)property.GetValue(this, null);
                permission.IsAllowed = false;
                if (permission.AccessControllList.Contains(currentUser.UserRole))
                {
                    permission.IsAllowed = true;
                }
                OnPropertyChanged(property.Name);
            }
        }
    
        #region INotifyPropertyChangedImplementation
        ...
        protected virtual void OnPropertyChanged(string propertyName)
        {
            ...
        }
        ...
        #endregion 
    }
    
    • Implements INotifyPropertyChanged (to use its properties directly in XAML)
    • uses Singleton-Pattern to have only one instance
    • has const strings for user roles defined in database. It's only one place in code where user roles are used as string values. If you need to update the role list - you should change only const list and initialization method (Permissions initialization).
    • subscribes to User.LoginChanged Event and updates IsAllowed properties of each Permission.
  3. Different converters (IValueConverter) to convert Permission object (set in XAML) to Visibility/IsEnabled Property (can post code if you need)

  4. XAML:

    <Image Visibility="{Binding Permissions.TestPhase, Converter={StaticResource PermissionToVisibility}}" ... />
    <TextBox IsEnabled="{Binding Permissions.WEwork, Converter={StaticResource PermissionToBoolean}}" ... />
    <Button Command="..." CommandParameter="{Binding Permissions.Manage}" ... />
    
    • no need to edit XAML if you redefine role permissions
    • you can handle Visibility/IsEnabled/Command.CanExecute

Now I can manage User roles in GUI freely, role permissions are defined only in code. But it wasn't my requirement to manage role permissions.
Good enough is that if I need to update/change role permissions I will do it in only one place in code.

If you have questions or recomendations - I'm open for a dialog.

like image 187
Shwed_Berlin Avatar answered Dec 22 '25 20:12

Shwed_Berlin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!