Application Context losing authentication on wpf app

Application Context losing authentication on wpf app

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


Curelom posted on Wednesday, August 01, 2007

I have a wpf app that loses authentication after a dataportal fetch.  What might cause this?  I was using webservices and changed to local and I'm still getting the problem.

In the App.xaml, I authenticate using Library.Security.PTPrincipal.Login("xxx", "yyy")

My login code

public static bool Login(string username, string password)

{

PTIdentity identity =

PTIdentity.GetIdentity(username, password);

if (identity.IsAuthenticated)

{

PTPrincipal principal = new PTPrincipal(identity);

Csla.ApplicationContext.User = principal;

}

return identity.IsAuthenticated;

}

I then open up a window and check the authentication using

Csla.ApplicationContext.User.Identity.IsAuthenticated

Everything is authenticated correctly.  I then use the CslaDataProvider to get a BusinessList

The window loads the data fine, but I notice that the checkbox IsSavable in the below code is unchecked.  When I try to change data in one of the textboxes, I debug into the setter of the object and Csla.ApplicationContext.User.Identity.IsAuthenticated reports false. Confused [*-)] Any ideas on what is going on?  I could reauthenticate, but that seems to be a lot of extra hits to the database each time I do a fetch.

<csla:CslaDataProvider x:Key="ticketList"

ObjectType="{x:Type hd:TicketList}"

FactoryMethod="GetUserActiveTicketList"

IsAsynchronous="False"

>

<csla:CslaDataProvider.FactoryParameters>

<system:String>test string</system:String>

</csla:CslaDataProvider.FactoryParameters>

</csla:CslaDataProvider>

...

<DockPanel DataContext="{Binding Source={StaticResource ticketList}}" >

...

<csla:ObjectStatus >

<csla:Authorizer Name="AuthPanel">

<csla:Validator>

<StackPanel FlowDirection="LeftToRight">

<StackPanel.Resources>

<Style TargetType="{x:Type TextBlock}">

<Setter Property="Margin" Value="3,5"/>

</Style>

<Style TargetType="{x:Type Button}">

<Setter Property="Margin" Value="3,5"/>

</Style>

</StackPanel.Resources>

<TextBlock>Tid:</TextBlock>

<TextBlock Text="{Binding Tid, Mode=OneWay}"></TextBlock>

<TextBlock>Sub Status:</TextBlock>

<TextBox Name="txtSubStatus"

Text="{Binding SubStatus, Converter={StaticResource IdentityConverter}}"></TextBox>

<TextBlock>Subject:</TextBlock>

<TextBox Text="{Binding Subject, Converter={StaticResource IdentityConverter}}"></TextBox>

<StackPanel Orientation="Horizontal">

<Button

Command="ApplicationCommands.Save"

CommandTarget="{Binding Source={StaticResource ticketList}, Path=CommandManager, BindsDirectlyToSource=True}"

HorizontalAlignment="Left" IsDefault="True">Save</Button>

<Button

Command="ApplicationCommands.Undo"

CommandTarget="{Binding Source={StaticResource ticketList}, Path=CommandManager, BindsDirectlyToSource=True}"

HorizontalAlignment="Left" IsCancel="True">Cancel</Button>

<CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsSavable}">IsSavable</CheckBox>

<CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsValid}">IsValid</CheckBox>

<CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsDirty}">IsDirty</CheckBox>

<CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsNew}">IsNew</CheckBox>

</StackPanel>

</StackPanel>

</csla:Validator>

</csla:Authorizer>

</csla:ObjectStatus>

 

 

RockfordLhotka replied on Wednesday, August 01, 2007

This is an ugly side-effect of some intended behavior by WPF... WPF is multi-threaded behind the scenes, but the principal object is a per-thread concept. So you can find that the thread doing your work doesn't have the principal at times.

I had to add an event to the data portal to provide a workaround. Clearly, the quicker I get the 3.0 ebook done the better Smile [:)]

If you look at PTWfp, in MainForm.xaml.cs you'll see where I hook the DataPortal_DataPortalInitInvoke event and make sure the current thread (the one running the data portal) has the correct principal (from a static/Shared field, which is thus global to all threads in the AppDomain).

Just search for all uses of _principal to see how the whole flow works.

Curelom replied on Wednesday, August 01, 2007

Thank you very much.  This is what I get for being on the bleeding edge. Wink [;)]  I can't wait for the ebook.

Curelom replied on Wednesday, August 01, 2007

I think this is getting me closer, but not there yet.

I've added the static _principal and the DataPortalInitInvoke (and even DataPortalInvoke and DataPortalComplete for good measure) to the window.  All 3 events are firing correctly and the CSLA remains in the thread for the duration of the events.  It apparently switches threads after the dataportal completes, so when the textbox calls the property setter of the business object, it doesn't see the Csla Application Context with the authenticated principal.  I can see the static _principal is still there.  Since the setters do not call into the data portal again to attach the static principal to the current thread, what would be the best way to accomplish this?  Is there another event I could hook into?

RockfordLhotka replied on Wednesday, August 01, 2007

One key aspect of what I do in PTWpf is that the login process is started and managed by UI code.

In other words, the PTPrincipal.Login() call occurs on the UI thread, which is the main application thread. It is possible that other actions behind the scenes occur on background threads, but the UI thread is somewhat special - WPF is actually single threaded at the UI level.

What this means is that the Thread.CurrentPrincipal is set by Csla.ApplicationContext.User on the UI thread, which is the main thread. All other threads should basically pick it up from there, and more importantly, all data binding will run on the UI thread (because it comes from the UI) so that is the most important thread to get set right in any case.

I do suggest checking the current code in svn, because I've improved the code slightly as I've been writing the ebook. The current code is more aggressive in setting the principal than the 3.0.1 implementation.

Curelom replied on Thursday, August 02, 2007

I'm using 3.0.2 (test).  I'm not using a window to login as I am grabbing information from windows authentication.  I've been experimenting to see which event I could put this in.  Windows events I have tried are Initialized, Activated, Loaded, ContentRendered, GotFocus.  All but GotFocus occur before the Csla context gets lost.  So I've put the code in GotFocus and it seems to be working fine.

Thank you

 

RockfordLhotka replied on Thursday, August 02, 2007

You are using Windows auth?

 

That is ambient, as in all threads automatically have access to WindowsPrincipal.

 

But that’s not to say that the WindowsPrincipal is automatically the current principal for your thread. That’s a policy setting on the AppDomain, and that policy defaults to GenericPrincipal. That’s standard .NET behavior.

 

Also make sure you properly set the CslaAuthentication token in your app.config file. The setting is the same as for all other UI types in Chapters 9-11.

 

Rocky

 

 

From: Curelom [mailto:cslanet@lhotka.net]
Sent: Thursday, August 02, 2007 2:37 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Application Context losing authentication on wpf app

 

I'm using 3.0.2 (test).  I'm not using a window to login as I am grabbing information from windows authentication.  I've been experimenting to see which event I could put this in.  Windows events I have tried are Initialized, Activated, Loaded, ContentRendered, GotFocus.  All but GotFocus occur before the Csla context gets lost.  So I've put the code in GotFocus and it seems to be working fine.

Thank you

 



Curelom replied on Thursday, August 02, 2007

Maybe I'm doing this wrong, but I wanted to use windows authentication so users wouldn't have another username/password to memorize, but I didn't want to use AD groups to assign rights as I would have to go through our Network Admin to make any changes.  So I created a custom Principal object based off Csla.Security.BusinessPrincipalBase and I use a database table for rights.

 

RockfordLhotka replied on Thursday, August 02, 2007

OK, that’s very valid too J

 

But if you do that, you are using custom auth, not really Windows auth – at least from a CSLA perspective.

 

So you still need to follow the basic pattern used in PTWpf

 

1.       Call your “login” method (probably called MyCustomPrincipal.Initialize()) as the main form starts so you can merge the WindowsPrincipal/Identity with the roles from your table to create and initialize your custom principal

2.       Store that value in your static _principal field

3.       Handle the DataPortalInitInvoke event and ensure that ReferenceEquals(Csla.ApplicationContext.User, _principal) on every call

 

The key is that steps 1 and 2 run as the main form is loading (constructor or load event) so they happen on the main UI thread.

 

Of course if you need to use the data portal to actually do step 1 (which is likely) then you need to _first_ initialize the current principal to an unauthenticated version of your custom principal and set _that_ to _principal so the data portal can make that first call.

 

Rocky

 

 

 

From: Curelom [mailto:cslanet@lhotka.net]
Sent: Thursday, August 02, 2007 3:12 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Application Context losing authentication on wpf app

 

Maybe I'm doing this wrong, but I wanted to use windows authentication so users wouldn't have another username/password to memorize, but I didn't want to use AD groups to assign rights as I would have to go through our Network Admin to make any changes.  So I created a custom Principal object based off Csla.Security.BusinessPrincipalBase and I use a database table for rights.

 



Curelom replied on Wednesday, November 21, 2007

This has been resolved in VS 2008 RTM Big Smile [:D]

 

Sorry, I wrote too soon.  This has not be fixed in RTM :(

As long as there has been some user interaction, such as a button click, before the login is called, it works.  If there has been no user interaction , the user gets lost from the application context.

 

Copyright (c) Marimer LLC