WinForms - ApplicationContext.User principal being lost

WinForms - ApplicationContext.User principal being lost

Old forum URL: forums.lhotka.net/forums/t/12226.aspx


andrew123 posted on Monday, November 18, 2013

I've recently taken on a legacy project which involves using CSLA with WinForms as a thick client (i.e. CSLA Client and Server within same application).

It's currently running CSLA 4.0.1 - I have tried upgrading to 4.3.14, but my issue is still not resolved (I can't upgrade to 4.5.x yet as I will need to convert about 500 overrides of Save to SaveAsync!)

The issue is regarding the custom Principal which is being lost.  This is using custom authentication.

During login, the custom principal is set using ApplicationContext.User.  The custom principal derives from Csla.Security.CslaPrincipal.  The identity on the custom principal derives from ReadOnlyBase<T>.

Whilst everything "works", I have noticed that at times that the ApplicationContext.User has reverted to a GenericPrincipal object.  This is on the same thread that the login took place (in this case the UI thread).  I cannot find any places where we are setting the Thread.CurrentPrincipal or ApplicationContext.User directly (except post-login).

The previous developers seem to have been aware of this because they have "solved" the issue with this piece of code.  But of course if you want to get the Principal without running a data operation you cannot trust it to be set.

Csla.DataPortal.DataPortalInitInvoke += DataPortal_DataPortalInitInvoke;

void DataPortal_DataPortalInitInvoke(object obj)

{

   if (!ReferenceEquals(Csla.ApplicationContext.User, s_CurrentPrincipal))

      Csla.ApplicationContext.User = s_CurrentPrincipal;

}

Where s_CurrentPrincipal is a static field which I can confirm DOES keep the current principal.  I have put a trace on this and found that this handler does not set the principal to the GenericPrincipal so I don't believe this is the problem (this is the "solution"!)

The app.config I have ensure has the following:

   <add key="CslaAuthentication" value="Custom" />

 

But the Principal is still being lost.  

Is there anything I am missing?  Would there be any reason that the Thread.Principal is being lost after it has been set?  This is pure WinForms, so HttpContext should not be involved.

JonnyBee replied on Monday, November 18, 2013

Hi, 

Is Csla.Xaml.dll or Csla.Web.dll present in your applications bin folder?  If so - make sure to remove these assemblies as they contain other specialized application contexts.

Csla.ApplicationContext.User should give this implementation in WindowsForms: 

      public void SetUser(IPrincipal principal)
      {
        Thread.CurrentPrincipal = principal;
      }
And presumably - this should set the CurrentPrincipal on the UI thread of the Windows Forms application. 

andrew123 replied on Monday, November 18, 2013

Hello

No, I just have the Csla.dll assembly in my output folder.

JonnyBee replied on Monday, November 18, 2013

Another reason for losing the principal is that your code uses the builtin BackgroundWorker or otherwise starts new threads.

There is a separate Csla.Threading.BackgroundWorker in CSLA that makes sure to move the "CurrentPrincipal" to a new thread.

So - to investigate this further:

1. Make sure that the Csla.ApplicationContext.User is set on the UI Thread.

2. Make sure to use the Csla.Threading.BackgroundWorker (if you use BackgroundWorker) to get the User transferred to background thread.

Does your code uses Task or Task Parallell Library?

JonnyBee replied on Monday, November 18, 2013

And if it still fails - are you able to provide us a sample that recreates this behavior?

andrew123 replied on Monday, November 18, 2013

Hi - thanks for your help.

Yes, the principal is being set on the UI thread by setting the ApplicationContext.User.  I can confirm that directly after setting this, the Thread.CurrentPrincipal is set with the correct Principal.

No, there are no BackgroundWorkers being used.  However, the first place that I can reproduce the lost principal is the firing of a Windows.Forms.Timer - is there a similar pattern that needs to be followed by these?

Yes, I have begun to use the Task Parallel Library (as opposed to the Background Worker - i.e. using the TaskScheduler.FromCurrentSynchronizationContext) but these are new and I believe the issue was still there before this.

I can have a go at isolating out the behaviour, but it may take me a little while....

Thanks again.

JonnyBee replied on Monday, November 18, 2013

That makes more sense though. It means that the Windows.Forms.Timer executes it's action on a new thread that has the default GenericIdentity. 

And even though you can check that the principal is set on the "current thread" is doesn't necessarily mean that the current thread is the UI thread!! 

Read this article:

http://msdn.microsoft.com/en-us/library/system.appdomain.setthreadprincipal(v=vs.110).aspx

And this question on StackOverflow:

http://stackoverflow.com/questions/4592692/set-currentprincipal-in-winforms-for-all-threads

There is also a new Csla.Threading.CslaTaskScheduler that you can use with TaskFactory that ensures the user and application context is transferred to the background thread.

 

 

andrew123 replied on Tuesday, November 19, 2013

Hmmm. I though that once you set the Thread.CurrentPrincipal, all subsequent threads took on that principal?  I may need to read up some more :)

The Timer executes its fired event on the UI thread - I can see the Thread.CurrentThread.ManagedThreadId is the same, so its not clear why that thread does not have the correct Principal.

I will try using the AppDomain.SetThreadPrincipal at the point of login and see if it fixes my issue - but I think you can only call this once per app domain so it doesn't allow me to switch to a different user in the same process (unless I get into AppDomains).

JonnyBee replied on Tuesday, November 19, 2013

andrew123

Hmmm. I though that once you set the Thread.CurrentPrincipal, all subsequent threads took on that principal?  I may need to read up some more :)

That is certainly not true. You only set the principal on the current thread (or the threads ExecutionContext?). 

About login - remember also that you may have a number of threads running already under a certain users context.You can use reflection to change the default principal for new threads - but there is no guaranteed way of changing the CurrentPrincipal on ALL RUNNING threads.

 

 

andrew123 replied on Wednesday, December 18, 2013

This turned out to be an issue with where the initial principal was being set.  The application was loading a Login Form from the Shown event of the main form.  This Login Form then set the principal upon successfully logging in.  

However, I discovered that once the Shown event had completed, it was at this point that the principal was reverting back to the Generic Principal. 

Moving everything to the Load event , rather than the Shown, fixed my issue.  

I don't know why this was (they are DevEx Forms too if it is specific to this) though!

Copyright (c) Marimer LLC