4
\$\begingroup\$

I'm handling user permission in a MVP Winforms application as follows. Here I'll give a full detail of my code as it will be helpful when answering to this question.

My user model has a list called permissions which holds the permissions of the current user. (eg: CAN_EDIT_ACCOUNT)

public class User
{
    //
    //
    List<stirng> Permissions = new List<string>();
}

In Programe.cs I instantiate required objects and services as follows. Please note that I'm using a single presenter for both login and main views. (Hope MVP doesn't stop me doing that)

Programe.cs

    static void Main()
    {
        frmLogin loginView = new frmLogin (); // Login form
        User userModel = new User();            
        DataService dataService = new DataService();
        UserPermissionService ups = new UserPermissionService();
        frmMain mainView = new frmMain(); // Application's main window

        LoginPresenter presenter = new LoginPresenter(loginView,mainView, userModel, dataService, ups);
        presenter.Show();            
    }

UserPermissionService is supposed to handle giving permission on request. At the creation of this service in the above code, its not aware of the user as the user is yet to login. So that Method Injection will be used.

There is a bool property for each module or feature that we need to control permission. If the respective permission is in the permissions list of the user, the property will be set to True.

public class UserPermissionService
{
    private User _user = null;

    public void InjectUser(User user) // User injected
    {
        _user = user;
    }

    public bool CanAccessBankAccountModule
    { 
        get { return _user.Permissions.Contains("BANKACCOUNT"); } 
    }
    //
    //
}

LOGIN

When the user presses OK button after supplying user credentials, it will validate it as follows. LoginPresenter will listen to the OnValidatePassword event and if its valid then the main window will be loaded and the user will be injected to the UserPermissionService. (please see ValidatePassword() method in LoginPresenter code)

public partial class frmLogin : Form
{
 private void btnOK_Click(object sender, EventArgs e)
    {
        OnValidatePassword(sender, e);
    }
}

PERMISSION HANDLING

Let's say the main form has buttons for every module and we need to enable a button if the user has permissions to access that particular module (eg. btnBankAccountModule should be enabled to access BANK ACCOUNT module if the user has permission). There is a public bool property for every button in the form and in the Load event, OnControlPermission will be fired and LoginPresenter will listen to this event and it will set the properties True or False based on the details provided by the UserPermissionService. (please see ControlPermission() method in the LoginPresenter code)

 public partial class frmMain : Form
 {
      public bool CanAccessBankAccountModule { get; set; }

      private void Form1_Load(object sender, EventArgs e)
      {
          OnControlPermission(sender, e);
          btnBankAccountModule.Enabled = CanAccessBankAccountModule; 
          //
          //
      }
 }


 class LoginPresenter
 {
      frmLogin _LoginView;
      frmMain _MainView;
      User _Model;
      DataService _DataService;
      UserPermissionService _UPS;

     public LoginPresenter( frmLogin loginView, frmMain mainView, User model, DataService dataService, UserPermissionService ups )
        {
            _LoginView = loginView;
            _MainView = mainView;
            _Model = model;
            _DataService = dataService;
            _UPS = ups;

            WireUpEvents();
        }

        void _MainView_OnControlPermission(object sender, EventArgs e)
        {
            ControlPermission();
        }

        private void ControlPermission()
        {
            _MainView.CanAccessBankAccountModule = _UPS.CanAccessBankAccountModule;
            //
            //
        }

        void _View_OnValidatePassword(object sender, EventArgs e)
        {
            ValidatePassword();
        }

        private void ValidatePassword()
        {
            var hash = Encryption.GetHash(_LoginView.UserID, _LoginView.Password);
            var user = _DataService.GetPermissions(_DataService.GetUser(_LoginView.UserID));
            if (user != null)
            {
                _Model = user;
                var hashInDB = _Model.PassWord;

                if (hash != hashInDB)
                {
                    MessageBox.Show("Invalid password");
                    _LoginView.Valid = false;
                }
                else
                {
                    _UPS.InjectUser(_Model);
                    _MainView.PermissionsService = _UPS; //Optional
                    _MainView.ShowDialog();
                }
            }
            else
            {
                MessageBox.Show("Invalid user name");
            }
        }

        public void Show()
        {
            _LoginView.ShowDialog();
        }
    }

Every thing seems fine up to the main window. My problem is, how should I make the UserPermissinService available to next level of UIs? (eg. To frmBankAccount view)

To provide the above service...

  • Should my LoginPresenter communicate with BankAccountPresenter?

OR

  • Could I have a public Property in main form as follows, which will then be passed to subsequent forms?

    public UserPermissionService PermissionsService { get; set; }
    

    This property will be set by the LoginPresenter with current instance of the UserPermissionService. (Please see this optional line in LoginPresenter code; _MainView.PermissionsService = _UPS; // )

    Then, when the next presenter is instantiated in the main form... we can pass a reference to the current UserPermssionService instance, to the next presenter.

    var BankAccountPresenter = new BankAccountPresenter(modle, view, dataService, PermissionsService);
    BankAccountPresenter.Show();
    

    Is it Okay?

OR

  • Is it better to instantiate an new instance of UserPermissionService in the Main form ? Then how should I inject the current user to the permission service?

Note : I am not familiar with IOC, so I'd appreciate if you could give an other explanation which would go inline with my current work?

\$\endgroup\$
2
\$\begingroup\$

I am looking at this Method

    private void ValidatePassword()
    {
        var hash = Encryption.GetHash(_LoginView.UserID, _LoginView.Password);
        var user = _DataService.GetPermissions(_DataService.GetUser(_LoginView.UserID));
        if (user != null)
        {
            _Model = user;
            var hashInDB = _Model.PassWord;

            if (hash != hashInDB)
            {
                MessageBox.Show("Invalid password");
                _LoginView.Valid = false;
            }
            else
            {
                _UPS.InjectUser(_Model);
                _MainView.PermissionsService = _UPS; //Optional
                _MainView.ShowDialog();
            }
        }
        else
        {
            MessageBox.Show("Invalid user name");
        }
    }

and it doesn't appear that you really use _Model anywhere else, so I was thinking that you could just remove _Model = user; and use user wherever you were using _Model.

One other thing in this snippet, you should probably use a positive conditional for the if statements, so we just flip stuff around.

Like this

private void ValidatePassword()
{
    var hash = Encryption.GetHash(_LoginView.UserID, _LoginView.Password);
    var user = _DataService.GetPermissions(_DataService.GetUser(_LoginView.UserID));
    if (user != null)
    {
        var hashInDB = user.PassWord;

        if (hash == hashInDB)
        {
            _UPS.InjectUser(user);
            _MainView.PermissionsService = _UPS; //Optional
            _MainView.ShowDialog();
        }
        else
        {
            MessageBox.Show("Invalid password");
            _LoginView.Valid = false;

        }
    }
    else
    {
        MessageBox.Show("Invalid user name");
    }
}

It just makes the code slightly cleaner.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.