# Monday, 09 August 2010

Note: This expands on the code created in my previous post: Implementing Attribute Based Domain Validation In a WPF MVVM Application

* Download Complete Source Code

Continuing on my WPF MVVM Adventures... My next task was to implement security on my composite application.  Unfortunately, as is all to often the case, there where still business decisions to be made concerning the granularity of the security {is field level required} and also the source of record {LDAP, roll-our-own}.  Not wanting to loose focus on the security piece and knowing I couldn’t sit around and do nothing I decided to tackle the one piece I knew that was constant…I was going to need a UI for logging in a user.

I started this process by creating a simple/standard log on window.

image  image 

LogOn.xaml
  1. <Window x:Class="CompositeAppPoc.Shell.LogOn"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         Title="Log On" SizeToContent="WidthAndHeight"
  5.         WindowStartupLocation="CenterScreen" WindowStyle="None"
  6.         ResizeMode="NoResize" MinWidth="300" BorderThickness="4" AllowsTransparency="True">
  7.  
  8.     <Window.Resources>
  9.         <Style TargetType="TextBox">
  10.             <Setter Property="Margin" Value="4"/>
  11.             <Setter Property="MinWidth" Value="100"/>
  12.         </Style>
  13.         <Style TargetType="PasswordBox">
  14.             <Setter Property="Margin" Value="4"/>
  15.             <Setter Property="MinWidth" Value="100"/>
  16.         </Style>
  17.         <Style TargetType="Button">
  18.             <Setter Property="Margin" Value="4"/>
  19.             <Setter Property="MinWidth" Value="75"/>
  20.         </Style>
  21.     </Window.Resources>
  22.  
  23.     <Grid>
  24.         <Grid.ColumnDefinitions>
  25.             <ColumnDefinition Width="Auto"/>
  26.             <ColumnDefinition/>
  27.         </Grid.ColumnDefinitions>
  28.         <Grid.RowDefinitions>
  29.             <RowDefinition Height="Auto"/>
  30.             <RowDefinition Height="Auto"/>
  31.             <RowDefinition Height="Auto"/>
  32.             <RowDefinition Height="Auto"/>
  33.             <RowDefinition/>
  34.         </Grid.RowDefinitions>
  35.         <!-- Header -->
  36.         <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2">
  37.             <Label HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
  38.                    FontWeight="Bold" FontSize="16" Content="Log On">
  39.             </Label>
  40.         </StackPanel>
  41.         <!-- Main Controls -->
  42.         <Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Content="User Name"/>
  43.         <TextBox Grid.Column="1" Grid.Row="1" GotFocus="CredentialsFocussed" x:Name="txtUsername"/>
  44.         <Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Content="Password"/>
  45.         <PasswordBox Grid.Column="1" Grid.Row="2" GotFocus="CredentialsFocussed" x:Name="txtPassword" />
  46.         <Label Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Center" Foreground="Red" FontStyle="Italic" Visibility="{Binding ShowInvalidCredentials}" Content="*Invalid User Name and/or Password"/>
  47.         <StackPanel Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="2" Orientation="Horizontal" VerticalAlignment="Bottom" Margin="5" HorizontalAlignment="Right" Height="Auto">
  48.             <Button Content="Log-on" Click="LogonClick" IsDefault="True"/>
  49.             <Button Content="Cancel" IsCancel="True"/>
  50.         </StackPanel>
  51.  
  52.     </Grid>
  53.  
  54. </Window>

LogOn.xaml.cs
  1. using System.ComponentModel;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Controls.Primitives;
  5.  
  6. namespace CompositeAppPoc.Shell {
  7.  
  8.     public partial class LogOn : Window, INotifyPropertyChanged {
  9.  
  10.         #region Private Fields
  11.  
  12.         private int _attempts;
  13.  
  14.         #endregion
  15.  
  16.         #region Public Properties
  17.  
  18.         public int Attempts {
  19.             get { return _attempts; }
  20.             set {
  21.                 if (value != _attempts) {
  22.                     _attempts = value;
  23.                     OnPropertyChanged("Attempts");
  24.                 }
  25.             }
  26.         }
  27.  
  28.         public Visibility ShowInvalidCredentials {
  29.             get {
  30.                 if (_attempts > 0) {
  31.                     return Visibility.Visible;
  32.                 }
  33.                 return Visibility.Hidden;
  34.             }
  35.         }
  36.  
  37.         public string UserName {
  38.             get { return txtUsername.Text; }
  39.         }
  40.  
  41.         public string Password {
  42.             get { return txtPassword.Password; }
  43.         }
  44.  
  45.         #endregion
  46.  
  47.         public LogOn() : this(string.Empty,string.Empty) {}
  48.  
  49.         public LogOn(string userName, string password) {
  50.             InitializeComponent();
  51.             DataContext = this;
  52.             txtUsername.Focus();
  53.             txtUsername.Text = userName;
  54.             txtPassword.Password = password;
  55.         }
  56.  
  57.         #region INotifyPropertyChanged Members
  58.  
  59.         public event PropertyChangedEventHandler PropertyChanged {
  60.             add { PropertyChangedEvent += value; }
  61.             remove { PropertyChangedEvent -= value; }
  62.         }
  63.  
  64.         #endregion
  65.  
  66.         private void LogonClick(object sender, RoutedEventArgs e) {
  67.             DialogResult = true;
  68.             Close();
  69.         }
  70.  
  71.         private void CredentialsFocussed(object sender, RoutedEventArgs e) {
  72.             TextBoxBase tb = sender as TextBoxBase;
  73.             if (tb == null) {
  74.                 PasswordBox pwb = sender as PasswordBox;
  75.                 pwb.SelectAll();
  76.             }
  77.             else {
  78.                 tb.SelectAll();
  79.             }
  80.         }
  81.  
  82.         private event PropertyChangedEventHandler PropertyChangedEvent;
  83.  
  84.         protected void OnPropertyChanged(string prop) {
  85.             if (PropertyChangedEvent != null)
  86.                 PropertyChangedEvent(this, new PropertyChangedEventArgs(prop));
  87.         }
  88.     }
  89. }

Now I have my Log On window but it doesn’t do anything.  I need to tell my application to show that window first.  Your first thought would be to go into the project properties and set LogOn as the startup object, but once you got in there you would notice that there is currently no startup object defined and this is by design.  Currently, because we are working with a composite application, all of that is handles by our Bootstrapper which is kicked off from App.xaml in the OnStartUp event.

App.xaml.cs (Old)
  1. using System.Windows;
  2.  
  3. namespace CompositeAppPoc.Shell {
  4.  
  5.     public partial class App : Application {
  6.  
  7.         protected override void OnStartup(StartupEventArgs e) {
  8.             base.OnStartup(e);
  9.             new Bootstrapper().Run();
  10.         }
  11.  
  12.     }
  13. }

We need to modify App.xaml.cs to:

  1. Show LogOn dialog
  2. Process result from LogOn dialog
    1. If ‘Cancel’ is clicked then close the application
    2. If ‘OK’ is clicked
      1. Perform authorization
        1. If authorized
          1. Run the bootstrapper
        2. If not authorized
          1. If exceeded the number of allowed attempts close the application
          2. Else…add 1 to the attempts, store username and password, and re-show the LogOn
App.xaml.cs (New)
  1. using System.Collections.Generic;
  2. using System.Windows;
  3. using CompositeAppPoc.Infrastructure.Entities;
  4. using CompositeAppPoc.Infrastructure.Services;
  5.  
  6. namespace CompositeAppPoc.Shell {
  7.  
  8.     public partial class App : Application {
  9.  
  10.         #region Private Fields
  11.  
  12.         private readonly AuthenticationService _authenticationService = new AuthenticationService();
  13.         private int _attempts;
  14.         private string _userName = "";
  15.         private string _pass = "";
  16.  
  17.         #endregion
  18.  
  19.         #region Private Methods
  20.         
  21.         private void ShowLogOn() {
  22.             var logon = new LogOn(_userName,_pass);
  23.             logon.Attempts = _attempts;
  24.             bool? res = logon.ShowDialog();
  25.             if (!res ?? true) {
  26.                 Shutdown(1);
  27.             }
  28.             else {
  29.                 AuthenticationContext ac = _authenticationService.Authenticate(logon.UserName, logon.Password);
  30.                 if (ac.IsAuthorized) {
  31.                     StartUp(ac.AllowedModules);
  32.                 }
  33.                 else {
  34.                     if (logon.Attempts > 2) {
  35.                         MessageBox.Show("Application is exiting due to invalid credentials", "Application Exit", MessageBoxButton.OK, MessageBoxImage.Error);
  36.                         Shutdown(1);
  37.                     }
  38.                     else {
  39.                         _attempts += 1;
  40.                         _userName = logon.UserName;
  41.                         _pass = logon.Password;
  42.                         ShowLogOn();
  43.                     }
  44.                 }
  45.             }
  46.         }
  47.  
  48.         private static void StartUp(IList<string> allowedModules) {
  49.             Current.MainWindow = null;
  50.             Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
  51.             var bs = new Bootstrapper();
  52.             bs.Run();
  53.             bs.LoadSecuredModules(allowedModules);
  54.         }
  55.  
  56.         #endregion
  57.  
  58.         #region Public Methods
  59.  
  60.  
  61.         public App() {
  62.             Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
  63.             ShowLogOn();
  64.         }
  65.  
  66.         #endregion
  67.     }
  68. }

That’s it, now we are requiring logging in to our application.  You may notice that there are bits in there that we haven’t covered yet, AuthenticationContext etc., this was me planning for the actual authentication implementation and can be replaced with whatever code you need.

Stay tuned for the actual security implementation.

* Download Complete Source Code

Friday, 20 December 2013 09:30:54 (Central Standard Time, UTC-06:00)
orem wywalonych przej_cia.
Frodo le_a_ na Po_ytku rozmazane widzenie.
piankowym materacu Us_Ugi Ksi_Gowe; Http://Firmostan.Pl/1478-Audyt2Biznes-Fachowe-Biuro-Rachunkowe.Html, przykrytym burym kocem wojskowym, Wagner
odst_pi_ mu swoje kryj�wka dzikiego zwierz_cia, postrzelany tudzie_ poplamiony m�zgiem Kirpiczewa prycza
nie nadawa_ si_ do u_ytku.
Jakk mucha w smole milczeli.
Frodo, ostatnimi czasy natychmiast do tego stopnia najbli_szy wykrzyczenia wszelakie _al�w, gwa_townie nie
m�g_
wyszpera_ przydatnych s_�w. Le_a_ ze zrolowanym _piworem poni_ej mak�wk_,
s_.
Monday, 10 August 2015 11:09:13 (Central Daylight Time, UTC-05:00)
that's not MVVM mate ...
anonymouse
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview