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. 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>
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
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.
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?
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.
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
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
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.
This has been resolved in VS 2008 RTM
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