C# + WPFで、GenericPrincipalを使った認証と承認を行い、画面の表示を制限する

引き続き、Microsoft.TeamFoundation.MVVM名前空間を使って作る、WPFアプリの話です。

今回は、GenericPrincipalを使った認証と承認を行い、画面の表示を制限してみました。

 
2015/12/10 追記 ここから

公式Blogに以下の記事が掲載されましたので、Microsoft.TeamFoundation.MVVMの使用前に記事を確認してみてください。
Microsoft.TeamFoundation.MVVM 名前空間の利用について - Visual Studio サポート チーム blog - Site Home - MSDN Blogs

2015/12/10 追記 ここまで

 

環境

 

仕様

以下の通りです。

  • ログイン画面でユーザー名を入力
  • 管理者ユーザーの場合、全部のボタンが表示
  • それ以外の場合、一部のボタンの表示を制限

 
なお、ボタン表示については、

の2パターンを用意し、どのように表示が異なるのかを試してみました。

 

ログイン画面

View

LoginView.xamlに実装します。

 

定型的なところ

以下の項目を追加しますが、定型なので省略します。

  • Window要素
  • Window.DataContext
  • Window.Resources

 

PasswordBoxへのデータバインディング

今回、ユーザー名の入力欄にはPasswordBoxを使いました(WPFでPasswordBoxを使ってみたかっただけで、特に深い意味はありません)。

PasswordBoxには以下のデータバインディングを行います。

  • コマンド (Pathで指定)
  • Enterキーを押した時にコマンドを実行 (Keyで指定)
  • コマンドに渡すパラメータの指定 (CommandParameterで指定)
<PasswordBox Grid.Row="1" Name="passwordBox">
    <PasswordBox.InputBindings>
        <KeyBinding Command="{Binding Path=LoginCommand}" Key="Enter" 
                    CommandParameter="{Binding ElementName=passwordBox}"/>
    </PasswordBox.InputBindings>
</PasswordBox>

 
参考:

 

起動時にPasswordBoxへフォーカスを当てる

以下を参考にGridへと設定しました。
WPFは初期フォーカスをXamlで設定できるでやんの。 - ABCの海岸で

<Grid FocusManager.FocusedElement="{Binding ElementName=passwordBox}">

 

ViewModel

LoginViewModel.cs に実装します。

 

PasswordBoxの値の参照

今回はViewにて、PasswordBoxのCommandParameterにPasswordBoxの値を設定するようにしています。

そのため、ViewModelのCommandの実装部分、ExecuteLoginCommand メソッドにて、以下のような感じで参照できます。

private void ExecuteLoginCommand(object x)
{
    var passwordBox = x as System.Windows.Controls.PasswordBox;
    var value = passwordBox.Password
}

 
なお、PasswordBoxのPasswordプロパティについては、以下にある通り直接な参照は控えたほうがいいかもしれません。

 
ただ、SecurePasswordのプロパティの扱いは少々面倒な上、今回はパスワードではなくユーザー名を渡しているだけなので、手抜きでPasswordプロパティの参照にしています。

 

ユーザーに対するRoleの割り当て

PasswordBoxのユーザー名に応じてRoleを割り当てます。そのRoleを元に、LoggedinViewの表示をLoggedinViewModelで制限します。

MSDNにある通り、GenericPrincipal オブジェクトと GenericIdentity オブジェクトを作成して、CurrentPrincipalに割り当てます。
方法 : GenericPrincipal オブジェクトと GenericIdentity オブジェクトを作成する - MSDN

var identity = new GenericIdentity(passwordBox.Password);
var principal = new GenericPrincipal(identity, GetRoles(passwordBox.Password));
Thread.CurrentPrincipal = principal;

 

ログイン後の画面

View

LoggedinView.xaml に実装します。

XAMLには

  • PasswordBoxで入力したユーザー名を表示するため、TextBlockのTextプロパティ
  • ButtonのCommandプロパティ
  • ButtonのVisibilityプロパティ

を記述します。

<TextBlock Grid.Row="0" Text="Loggedin User:"/>
<TextBlock Grid.Row="1" Text="{Binding Path=LoggedinUser}"/>
<Button Grid.Row="2" Content="All by RelayCommand" Command="{Binding Path=AllCommand}"/>
<Button Grid.Row="3" Content="Limit by RelayCommand" Command="{Binding Path=LimitCommand}"/>
<Button Grid.Row="4" Content="All by Visibility" Visibility="{Binding Path=VisibilityAll}"/>
<Button Grid.Row="5" Content="Limit by Visibility" Visibility="{Binding Path=VisibilityLimit}"/>

 

ViewModel

LoggedinViewModel.csに実装します。

 

PasswordBoxに入力したユーザー名の取得

以下の記事のコメントにもある通り、Thread.CurrentPrincipal.Identity.Nameには認証されていなくても値が設定されていることがあるため、認証されているかを判断した上でユーザー名を取得します。
リッチクライアントとロールベースセキュリティ - Kazzzの日記

if (_loggedinUser == null && Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
    _loggedinUser = Thread.CurrentPrincipal.Identity.Name;
}

 

Commandプロパティで、RelayCommandオブジェクトによる表示制御

以下の感じのようなコードで、RelayCommandコンストラクタの第二引数にboolを渡すことで、Viewにおけるボタン表示のenabled/disabledを切り替えています。

Roleに属しているかは、Thread.CurrentPrincipal.IsInRole("Role1"); で判断しています。

private ICommand _allCommand;
public ICommand AllCommand
{
    get
    {
        if (_allCommand == null)
        {
            _allCommand = new Microsoft.TeamFoundation.MVVM.RelayCommand(ExecuteAllCommand, CanExecuteAllCommand);
        }
        return _allCommand;
    }
}

private void ExecuteAllCommand(object x) { }

private bool CanExecuteAllCommand(object x)
{
    //  認証されて`Role1`に属していたらtrue
    return Thread.CurrentPrincipal.Identity.IsAuthenticated && Thread.CurrentPrincipal.IsInRole("Role1");
}

 

Visibilityプロパティによる制御

そのままです。

public System.Windows.Visibility VisibilityAll
{
    get
    {
        //  認証されて`Role1`に属していたらVisible, そうでなければHidden
        if (Thread.CurrentPrincipal.Identity.IsAuthenticated && Thread.CurrentPrincipal.IsInRole("Role1"))
        {
            return System.Windows.Visibility.Visible;
        }
        else
        {
            return System.Windows.Visibility.Hidden;
        }
    }
}

 

作成した画面イメージ

ログイン画面

f:id:thinkAmi:20140826044239p:plain

 

管理者ユーザーの場合

全てのボタンが見えています。

f:id:thinkAmi:20140826044305p:plain

 

その他の場合

RelayCommandの場合はDisabledになっているだけですが、VIsibilityはHideになっています。

f:id:thinkAmi:20140826044316p:plain

 

ソースコード

GitHubにあげておきました。
CSharp-Sample/MVVMApp/GenericPrincipalMVVM at master · thinkAmi/CSharp-Sample · GitHub

 

参考資料

ロールベースセキュリティ

 

PasswordBox

 

書籍

VB.NETとWindowsFormがメインですが、.NET Frameworkの認証と承認についてまとまっており、とても参考になりました。

Visual Basic 2008 逆引きレシピ[Windows アプリケーション編] (PROGRAMMER’S RECiPE)

Visual Basic 2008 逆引きレシピ[Windows アプリケーション編] (PROGRAMMER’S RECiPE)