Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting user credentials in WPF w/Unity and MVVM

Tags:

c#

mvvm

wpf

I am having a terrible go at this despite having done something similar in a Windows Application. I am working on a WPF application (Prism, Unity, MVVM) and I have just completed the login view. Once the user's credentials have been validated against a table in SQL Server I do this:

 Thread.CurrentPrincipal = user.GenericPrincipal();

The user class is defined as such:

public class ApplicationIdentity : GenericIdentity, IApplicationIdentity
{
    public string UserName { get; set; }
    public bool Authenticated { get; set; }

    public ICollection<IModuleProperties> Modules { get; set; }
    public ICollection<IViewProperties> Views { get; set; }

    public ApplicationIdentity(string userName, IAuthenticatedUser authenticatedUser)
        : base(userName)
    {
        UserName = userName;
        Authenticated = authenticatedUser.Authenticated;
        Modules = authenticatedUser.Modules;
        Views = authenticatedUser.Views;
    }

    public IPrincipal GenericPrincipal()
    {
        return new GenericPrincipal(this, null);
    }
}

The problem I am running into is that almost immediately after the login screen is dismissed I make this call:

 var currentUser = (IApplicationIdentity)Thread.CurrentPrincipal.Identity;

Which throws an exception that Identity can't be cast to type IApplicationIdentity, and I am not sure what I am missing. All of the SO / Google articles I have read so far around this have all solved this problem by authenticating the user against AD, but that doesn't work in my scenario.

The problem I am trying to solve here is simply to persist the currently logged on user and which Modules and Views they should have access to. If there is a better way to achieve this outside of setting the CurrentPrincipal I am completely open to other solutions. Thanks for any help you might be able to provide!

EDIT (SOLUTION):

I just want to close the loop on what the solution was. What follows is a bit of tutorial, so it is a bit lengthy but should be helpful to anyone stumbling across it. The accepted answer suggested that I inject the Unity container, register an instance of my object, and work with it from there. I agree that this is probably the correct approach, but it required me to "hack" on my Bootstrapper a bit. Before I get to the Bootstrapper logic though, I should probably go over a bit of the prep work involved.

In my original approach my Login view was not registered with my container, because the Login view was instantiated prior to my Bootstrapper running (App.xaml opened the Login view.)

The first thing that I did was make my View and ViewModel injectable ala:

    public Login(ILoginViewModel viewModel)
    {
        InitializeComponent();
        DataContext = viewModel;
    }

    public LoginViewModel(IUnityContainer container)
    {
        Container = container;
    }

Again: this should be familiar to anyone that has worked with Unity (or IoC in general). I then needed to register my current user object with Unity once a user had been authenticated:

    private void Login(object obj)
    {
        ...
        if (user.Authenticated)
        {
            Container.RegisterInstance("CurrentUser", user); 
        }
        ...
    }

Anyone that has followed any of the Prism/Unity/MVVM articles that abound on the internet is probably familiar with the following method:

    protected override IModuleCatalog CreateModuleCatalog()
    {
        var catalog = new ModuleCatalog();
        catalog.AddModule(typeof (CoreModule));
        catalog.AddModule(typeof (CoreModule2));
        catalog.AddModule(typeof (CoreModule3));
        return catalog;
    }

This method is straightforward enough, but in a real world scenario the modules a user has access to will probably be dynamic rather than static (or a combination of both.) CreateModuleCatalog() is called in the Bootstrapper Run() method ahead of InitializeShell(). In my case I still have a static module that all users would have access to (regardless of authorization level), but (in my opinion) it would feel "anti-patternish" to instantiate the Login view from this method (not to mention register the types with Unity.) Thus my CreateModuleCatalog() became:

    protected override IModuleCatalog CreateModuleCatalog()
    {
        var catalog = new ModuleCatalog();
        catalog.AddModule(typeof(CoreModule));
        return catalog;
    }

I opted to use my override of InitializeShell() to register my Login types, display the Login view, etc. I ultimately wound up with this for an implementation:

    protected override void InitializeShell()
    {
        base.InitializeShell();
        Container.RegisterType(typeof (Login), "LoginView");
        Container.RegisterType<ILoginViewModel, LoginViewModel>();

        Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
        ShowLogOn();

        Application.Current.MainWindow = (Window)Shell;
        Application.Current.MainWindow.Show();
    }

ShowLogOn() will Shutdown the application if the user eventually hits the Cancel button on the Login view, so I could have probably placed all of the code from ShowLogOn() up prior to the call to base.InitializeShell() so that needless code isn't run. ShowLogOn() looks exactly like you might expect it to look:

    private void ShowLogOn()
    {
        var login = Container.Resolve<Login>();
        var dialogResult = login.ShowDialog();

        if (!dialogResult.Value)
        {
            Application.Current.Shutdown(1);
        }
        else
        {
            LoadAuthorizedModules();
            Application.Current.MainWindow = null;
            Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
        }
    }

If the user cancels out of my Login view the dialog result will be false and the application is shutdown. If they have successfully authenticated though, we now need to load the modules that they are permitted to see. This is also pretty straightforward:

    private void LoadAuthorizedModules()
    {
        var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser");
        foreach (var module in currentUser.Modules)
        {
            var moduleAssembly = Assembly.Load(module.AssemblyName);
            var loadingModule = moduleAssembly.GetType(module.Type);

            ModuleCatalog.AddModule(new ModuleInfo
                {
                    ModuleName = loadingModule.Name,
                    ModuleType = loadingModule.AssemblyQualifiedName
                });
        }
    }

This method warrants a bit more explanation. First take a look at this line as it might be confusing: var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser"); notice that I did not explicitly register the type IApplicationIdentity anywhere in my code! It was, however, implicitly registered when I did this: Container.RegisterInstance("CurrentUser", user); technically I could have written the previous statement as: Container.RegisterInstance<IApplicationIdentity>("CurrentUser", user); but that is redundant to me so do what ever you feel most comfortable with.

The Modules property of IApplicationIdentity contains a collection of objects that contain information about modules the current user has access to. module.AssemblyName is the name of the physical assembly that my custom module type lives in, and module.Type is the Type of the module.

As you can see: once the type has been loaded through the power of reflection, we need to add it to the Unity Bootstrappers ModuleCatalog property which is straight forward. Assuming everything has gone correctly, execution should return to your InitializeShell method, and your Shell should now start up.

This was pretty lengthy, but I hope that someone finds it useful. Also, I would be interested in any opinions on how to make this code "better". Thanks!

like image 464
dparsons Avatar asked Nov 10 '22 07:11

dparsons


1 Answers

You can check out this question if you want to store it in the thread - https://stackoverflow.com/a/6699699/1798889. But if you just want to have access to the IApplicationIdentity. I would suggest you just register that instance with unity.

// IUnityContainer can be injected by unity 
IUnityContainer container;

// Register this instance each time now you call for IApplicationIdentity this object will be returned
container.RegisterInstance(typeof (IApplicationIdentity), user.GenericPrincipal());

With this now you can inject IApplicationIdentity and unity will fulfill it each time you need it. You don't need to worry about the threads.

like image 182
CharlesNRice Avatar answered Nov 14 '22 22:11

CharlesNRice