ユーザーにロールを割り当てる (C#)

作成者: Scott Mitchell

Note

この記事を作成した後で、ASP.NET メンバーシップ プロバイダーよりも ASP.NET Identity のほうが適切になりました。 この記事を作成した時点で紹介したメンバーシップ プロバイダーではなく、ASP.NET Identity プラットフォームを使うように、アプリを更新することをお勧めします。 ASP.NET メンバーシップ システムと比べると、ASP.NET Identity には次のような多くの利点があります:

  • パフォーマンスの向上
  • 拡張性とテストの容易性の向上
  • OAuth、OpenID Connect、2 要素認証のサポート
  • クレームベースの ID のサポート
  • ASP.Net Core との相互運用性の向上

コードのダウンロードまたは PDF のダウンロード

このチュートリアルでは、どのユーザーがどのロールに属するかの管理に役立つ 2 つの ASP.NET ページを作成します。 最初のページに含める機能は、特定のロールに属しているユーザーの表示、特定のユーザーが属しているロールの表示、および特定のロールに対する特定のユーザーの割り当てや削除です。 2 つ目のページでは、CreateUserWizard コントロールを拡張して、新規作成されたユーザーが属するロールを指定するステップを含めます。 これは、管理者が新しいユーザー アカウントを作成できるという状況で役立ちます。

はじめに

前のチュートリアルでは、Roles フレームワークと SqlRoleProvider について確認しました。Roles クラスを使ってロールを作成、取得、削除する方法を説明しました。 ロールの作成と削除に加え、ロールに対してユーザーを割り当てまたは削除できる必要があります。 残念ながら、ASP.NET には、どのユーザーがどのロールに属するかを管理するための Web コントロールは付属していません。 代わりに、これらの関連付けを管理するための独自の ASP.NET ページを作成する必要があります。 幸い、ロールに対するユーザーの追加と削除は簡単です。 Roles クラスには、1 人以上のユーザーを 1 つ以上のロールに追加するためのメソッドが多数含まれています。

このチュートリアルでは、どのユーザーがどのロールに属するかの管理に役立つ 2 つの ASP.NET ページを作成します。 最初のページに含める機能は、特定のロールに属しているユーザーの表示、特定のユーザーが属しているロールの表示、および特定のロールに対する特定のユーザーの割り当てや削除です。 2 つ目のページでは、CreateUserWizard コントロールを拡張して、新規作成されたユーザーが属するロールを指定するステップを含めます。 これは、管理者が新しいユーザー アカウントを作成できるという状況で役立ちます。

それでは始めましょう。

どのユーザーがどのロールに属しているかを一覧表示する

このチュートリアルで最初に行う必要があるのは、ユーザーをロールに割り当てることができる Web ページの作成です。 ユーザーをロールに割り当てる方法に注目する前に、まず、どのユーザーがどのロールに属しているかを特定する方法に目を向けましょう。 この情報の表示には、"ロール別" または "ユーザー別" という 2 つの方法があります。訪問者にロールの選択を可能にしてそのロールに属するすべてのユーザーが表示されるようにするか ("ロール別" 表示)、訪問者にユーザーの選択を求めてそのユーザーに割り当てられているロールが表示されるようにする ("ユーザー別" 表示) ことができます。

"ロール別" 表示は、訪問者が、特定のロールに属する一連のユーザーを知りたい場合に役立ちます。"ユーザー別" 表示は、訪問者が、特定のユーザーのロールを知る必要がある場合に最適です。 ページに "ロール別" および "ユーザー別" インターフェイスを両方含めてみましょう。

まず、"ユーザー別" インターフェイスを作成します。 このインターフェイスは、1 つのドロップダウン リストとチェック ボックスのリストから成ります。 ドロップダウン リストに、システム内の一連のユーザーが移入されます。チェックボックスで、それらのロールが列挙されます。 ドロップダウン リストからユーザーを選択すると、そのユーザーが属しているロールが確認されます。 このページにアクセスしたユーザーは、その後、チェックボックスを選択するか選択解除して、選択したユーザーを、対応するロールに対して追加または削除できます。

Note

ドロップダウン リストを使ったユーザー アカウントの一覧表示は、ユーザー アカウント数が何百にもなる場合がある Web サイトに適した選択肢ではありません。 ドロップダウン リストは、選択肢を示す比較的短いリストからユーザーが 1 つの項目を選択できるように設計されています。 リスト項目の数が増えるとすぐに扱いにくくなります。 ユーザー アカウントが多数になる可能性のある Web サイトを作成する場合は、ページング可能な GridView やフィルター可能なインターフェイス (訪問者に文字の選択を求めて、選択された文字で始まるユーザー名のユーザーのみを表示する) など、代わりのユーザー インターフェイスの使用を検討できます。

手順 1: "ユーザー別" ユーザー インターフェイスを作成する

UsersAndRoles.aspx ページを開きます。 ページの上部に、ActionStatus という名前の Label Web コントロールを追加し、その Text プロパティをクリアします。 この Label を使って、実行されたアクションに関するフィードバックを提供し、"User Tito has added to the Administrators role" (ユーザー Tito が管理者ロールに追加されました)、"User Jisun has been removed from the Supervisors role" (ユーザー Jisun がスーパーバイザ ロールから削除されました) などのメッセージを表示します。これらのメッセージを目立たせるために、Label の CssClass プロパティを "Important" に設定します。

<p align="center"> 

     <asp:Label ID="ActionStatus" runat="server" CssClass="Important"></asp:Label> 
</p>

次に、次のような CSS クラス定義を Styles.css スタイルシートに追加します:

.Important 
{ 
     font-size: large; 
     color: Red; 
}

この CSS 定義で、ブラウザーに、大きな赤いフォントを使ってラベルを表示するように指示します。 図 1 に、Visual Studio デザイナーでのこの効果を示します。

The Label's CssClass Property Results in a Large, Red Font

図 1: Label の CssClass プロパティの結果として大きな赤いフォントになる (クリックするとフルサイズの画像が表示されます)

次に、ページに DropDownList を追加し、その ID プロパティを UserList に、その AutoPostBack プロパティを True に設定します。 この DropDownList を使ってシステム内のすべてのユーザーを一覧表示します。 この DropDownList は、MembershipUser オブジェクトのコレクションにバインドされます。 DropDownList に MembershipUser オブジェクトの UserName プロパティを表示する (それをリスト項目の値として使う) ため、DropDownList の DataTextField および DataValueField プロパティを "UserName" に設定します。

DropDownList の下に、UsersRoleList という名前の Repeater を追加します。 この Repeater では、一連のチェックボックスとして、システム内のすべてのロールが一覧表示されます。 次の宣言型マークアップを使って Repeater の ItemTemplate を定義します:

<asp:Repeater ID="UsersRoleList" runat="server"> 
     <ItemTemplate> 
          <asp:CheckBox runat="server" ID="RoleCheckBox" AutoPostBack="true" 

               Text='<%# Container.DataItem %>' /> 
          <br /> 
     </ItemTemplate> 
</asp:Repeater>

ItemTemplate マークアップには、RoleCheckBox という名前の 1 つの CheckBox Web コントロールが含まれています。 この CheckBox の AutoPostBack プロパティは True に設定されており、Text プロパティは Container.DataItem にバインドされています。 データ バインド構文が単純に Container.DataItem となっているのは、Roles フレームワークでロール名のリストが文字列配列として返され、Repeater にバインドされるのはこの文字列配列であるためです。 データ Web コントロールにバインドされた配列の内容を表示するためにこの構文を使う理由については、このチュートリアルでは説明していません。 この理由について詳しくは、データ Web コントロールへのスカラー配列のバインドに関するページをご覧ください。

この時点での、"ユーザー別" インターフェイスの宣言型マークアップは次のようになります:

<h3>Manage Roles By User</h3> 

<p> 
     <b>Select a User:</b> 
     <asp:DropDownList ID="UserList" runat="server" AutoPostBack="True" 
          DataTextField="UserName" DataValueField="UserName"> 

     </asp:DropDownList> 
</p> 
<p> 
     <asp:Repeater ID="UsersRoleList" runat="server"> 
          <ItemTemplate> 
               <asp:CheckBox runat="server" ID="RoleCheckBox" AutoPostBack="true" 

                    Text='<%# Container.DataItem %>' /> 
               <br /> 
          </ItemTemplate> 
     </asp:Repeater> 
</p>

これで、ユーザー アカウントのセットを DropDownList に、ロールのセットを Repeater にバインドするコードを記述する準備ができました。 このページの分離コード クラスで、次のコードを使って、BindUsersToUserList という名前のメソッドと、BindRolesList という名前のもう 1 つのメソッドを追加します:

private void BindUsersToUserList() 
{ 
     // Get all of the user accounts 
     MembershipUserCollection users = Membership.GetAllUsers(); 
     UserList.DataSource = users; 
     UserList.DataBind(); 
}
 
private void BindRolesToList() 
{ 
     // Get all of the roles 
     string[] roles = Roles.GetAllRoles(); 
     UsersRoleList.DataSource = roles; 
     UsersRoleList.DataBind(); 
}

BindUsersToUserList メソッドでは、Membership.GetAllUsers メソッド を使ってシステム内のすべてのユーザー アカウントが取得されます。 これにより、MembershipUser インスタンスのコレクションである、MembershipUserCollection オブジェクトが返されます。 その後、このコレクションが UserList DropDownList にバインドされます。 このコレクションを構成する MembershipUser インスタンスには、さまざまなプロパティが含まれています (UserNameEmailCreationDateIsOnline など)。 UserName プロパティの値を表示するよう DropDownList に指示するために、UserList DropDownList の DataTextField および DataValueField プロパティが "UserName" に設定されていることを確認します。

Note

Membership.GetAllUsers メソッドには、2 つのオーバーロードがあります。1 つは入力パラメーターを受け付けずすべてのユーザーを返し、もう 1 つはページ インデックスとページ サイズの整数値を受け取りユーザーの指定されたサブセットのみを返します。 ページング可能なユーザー インターフェイス要素に大量のユーザー アカウントが表示されている場合、この 2 番目のオーバーロードを使うと、すべてではなくユーザー アカウントの的確なサブセットのみが返されるため、より効率的にユーザーをページングできます。

BindRolesToList メソッドでは、最初に、Roles クラスの GetAllRoles メソッド (システム内のロールを含む文字列配列を返す) が呼び出されます。 その後、この文字列配列は Repeater にバインドされます。

最終的に、これら 2 つのメソッドを、このページが最初に読み込まれるときに呼び出す必要があります。 Page_Load イベント ハンドラーに次のコードを追加します。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 
     } 
}

このコードを配置したら、ブラウザーでこのページにアクセスします。画面は図 2 のようになります。 すべてのユーザー アカウントがドロップダウン リストに移入されており、その下に、各ロールがチェック ボックスとして表示されています。 DropDownList と CheckBox の AutoPostBack プロパティを True に設定したため、選択したユーザーを変更するか、ロールを選択または選択解除すると、ポストバックが発生します。 しかし、これらのアクションに対処するコードをまだ記述していないため、何もアクションは実行されません。 次の 2 つの項で、これらのタスクに取り組みます。

The Page Displays the Users and Roles

図 2: ページにユーザーとロールが表示されている (クリックするとフルサイズの画像が表示されます)

選択したユーザーが属しているロールを確認する

ページが最初に読み込まれるとき、または訪問者がドロップダウン リストから新しいユーザーを選択するたびに、特定のロールのチェック ボックス (選択したユーザーが属しているロールのみ) が選択されるように UsersRoleList チェック ボックスを更新する必要があります。 これを実現するには、次のコードを含む、CheckRolesForSelectedUser という名前のメソッドを作成します:

private void CheckRolesForSelectedUser() 
{ 
     // Determine what roles the selected user belongs to 
     string selectedUserName = UserList.SelectedValue; 
     string[] selectedUsersRoles = Roles.GetRolesForUser(selectedUserName); 

     // Loop through the Repeater's Items and check or uncheck the checkbox as needed 

     foreach (RepeaterItem ri in UsersRoleList.Items) 
     { 
          // Programmatically reference the CheckBox 
          CheckBox RoleCheckBox = ri.FindControl("RoleCheckBox") as CheckBox; 
          // See if RoleCheckBox.Text is in selectedUsersRoles 
          if (selectedUsersRoles.Contains<string>(RoleCheckBox.Text)) 
               RoleCheckBox.Checked = true; 
          else 
               RoleCheckBox.Checked = false; 
     } 
}

上記のコードでは、最初に、選択したユーザーが誰であるかが特定されます。 その後、Roles クラスの GetRolesForUser(userName) メソッドを使って、指定したユーザーの一連のロールが文字列配列として返されます。 次に、Repeater の項目が列挙され、各項目の RoleCheckBox CheckBox がプログラムによって参照されます。 この CheckBox は、その対応するロールが selectedUsersRoles 文字列配列に含まれている場合のみ選択されます。

Note

ASP.NET バージョン 2.0 を使っている場合、selectedUserRoles.Contains<string>(...) 構文はコンパイルされません。 Contains<string> メソッドは、LINQ ライブラリに含まれており、これは ASP.NET 3.5 の新機能です。 まだ ASP.NET バージョン 2.0 を使っている場合は、代わりに Array.IndexOf<string> メソッドを使います。

CheckRolesForSelectedUser メソッドは、次の 2 つの場合に呼び出す必要があります。ページが最初に読み込まれるときと、UserList DropDownList の、選択されているインデックスが変更されるたびです。 したがって、Page_Load イベント ハンドラーからこのメソッドを呼び出します (BindUsersToUserListBindRolesToList の呼び出しの後)。 また、DropDownList の SelectedIndexChanged イベントのイベント ハンドラーを作成し、そこからこのメソッドを呼び出します。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 

          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 
          // Check the selected user's roles 
          CheckRolesForSelectedUser(); 
     } 
} 

... 

protected void UserList_SelectedIndexChanged(object sender, EventArgs e) 
{ 
     CheckRolesForSelectedUser(); 
}

このコードを配置したら、ブラウザーでこのページをテストできます。 ただし、UsersAndRoles.aspx ページには現在はユーザーをロールに割り当てる機能がないため、ロールがあるユーザーはいません。 ユーザーをロールに割り当てるためのインターフェイスはすぐに作成します。このコードが動作するという私の言葉を信じて後でそれを確認することも、この機能を今すぐテストするために aspnet_UsersInRoles テーブルにレコードを挿入して手動でユーザーをロールに追加することもできます。

ロールに対してユーザーを割り当てるか削除する

訪問者が UsersRoleList Repeater 内の CheckBox を選択または選択解除したときは、選択したユーザーを、対応するロールに対して追加または削除する必要があります。 CheckBox の AutoPostBack プロパティは現在 True に設定されています。これにより、Repeater 内の CheckBox が選択または選択解除されるたびにポストバックが発生します。 要するに、CheckBox の CheckChanged イベントのイベント ハンドラーを作成する必要があります。 この CheckBox は Repeater コントロールに含まれているため、イベント ハンドラーの仕組みを手動で追加する必要があります。 まず、次のように、分離コード クラスにそのイベント ハンドラーを protected メソッドとして追加します:

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 

}

このイベント ハンドラーのコードの記述については後で説明します。 しかし、まず、イベント処理の仕組みを完成させましょう。 Repeater の ItemTemplate 内の CheckBox から、OnCheckedChanged="RoleCheckBox_CheckChanged" を追加します。 この構文では、RoleCheckBox_CheckChanged イベント ハンドラーを RoleCheckBoxCheckedChanged イベントに結び付けています。

<asp:CheckBox runat="server" ID="RoleCheckBox" 
     AutoPostBack="true" 
     Text='<%# Container.DataItem %>' 
     OnCheckedChanged="RoleCheckBox_CheckChanged" />

最後のタスクは、RoleCheckBox_CheckChanged イベント ハンドラーを完成させることです。 まず、このイベントの発生元の CheckBox コントロールを参照する必要があります。これは、この CheckBox インスタンスで、その Text および Checked プロパティを介して、選択または選択解除されたロールが示されるためです。 選択されたユーザーの UserName と共にこの情報を使って、Roles クラスの AddUserToRole または RemoveUserFromRole メソッドを介してロールに対してそのユーザーを追加または削除します。

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 
     // Reference the CheckBox that raised this event 
     CheckBox RoleCheckBox = sender as CheckBox; 

     // Get the currently selected user and role 
     string selectedUserName = UserList.SelectedValue; 

     string roleName = RoleCheckBox.Text; 

     // Determine if we need to add or remove the user from this role 
     if (RoleCheckBox.Checked) 
     { 
          // Add the user to the role 
          Roles.AddUserToRole(selectedUserName, roleName); 
          // Display a status message 
          ActionStatus.Text = string.Format("User {0} was added to role {1}.", selectedUserName, roleName); 
     } 
     else 
     { 
          // Remove the user from the role 
          Roles.RemoveUserFromRole(selectedUserName, roleName); 
          // Display a status message 
          ActionStatus.Text = string.Format("User {0} was removed from role {1}.", selectedUserName, roleName); 

     } 
}

上記のコードでは、最初に、イベントの発生元の CheckBox (sender 入力パラメーターを介して使用可能) がプログラムによって参照されています。 CheckBox を選択した場合は、選択したユーザーが、指定したロールに追加されます。そうでない場合は、そのロールから削除されます。 どちらの場合も、ActionStatus Label で、先ほど実行したアクションの概要のメッセージが表示されます。

ブラウザーでこのページをテストします。 ユーザー [Tito] を選択してから、Tito を Administrators および Supervisors ロールの両方に追加します。

Tito Has Been Added to the Administrators and Supervisors Roles

図 3: Tito が Administrators および Supervisors ロールに追加された (クリックするとフルサイズの画像が表示されます)

次に、ドロップダウン リストからユーザー [Bruce] を選択します。 ポストバックがあり、Repeater の CheckBox が CheckRolesForSelectedUser を介して更新されます。 Bruce はまだどのロールにも属していないため、2 つのチェック ボックスは選択されていません。 次は、Bruce を Supervisors ロールに追加します。

Bruce Has Been Added to the Supervisors Role

図 4: Bruce が Supervisors ロールに追加された (クリックするとフルサイズの画像が表示されます)

CheckRolesForSelectedUser メソッドの機能をさらに確認するために、Tito や Bruce 以外のユーザーを選択します。 チェック ボックスの選択が自動的に解除されて、どのロールにも属さないことを意味していることに注目してください。 [Tito] に戻ります。 [Administrators] および [Supervisors] チェック ボックスが両方選択されています。

手順 2: "ロール別" ユーザー インターフェイスを作成する

この時点では、"ユーザー別" インターフェイスは完成しており、"ロール別" インターフェイスに取り組み始めるための準備ができています。 "ロール別" インターフェイスでは、ドロップダウン リストからロールを選択するようユーザーに求めるダイアログが表示され、そのロールに属する一連のユーザーが GridView に表示されます。

別の DropDownList コントロールを UsersAndRoles.aspx ページに追加します。 これを Repeater コントロールの下に配置し、それに RoleList という名前を付け、その AutoPostBack プロパティを True に設定します。 その下に GridView を追加し、それに RolesUserList という名前を付けます。 この GridView では、選択されたロールに属しているユーザーが一覧表示されます。 GridView の AutoGenerateColumns プロパティを False に設定し、TemplateField をそのグリッドの Columns コレクションに追加し、その HeaderText プロパティを "Users" に設定します。 TemplateField の ItemTemplate を定義して、Container.DataItem という名前の Label の Text プロパティにデータバインド式 UserNameLabel の値が表示されるようにします。

GridView を追加し構成した後の、"ロール別" インターフェイスの宣言型マークアップは次のようになります:

<h3>Manage Users By Role</h3> 
<p> 
     <b>Select a Role:</b> 

     <asp:DropDownList ID="RoleList" runat="server" AutoPostBack="true"></asp:DropDownList> 
</p> 
<p>      <asp:GridView ID="RolesUserList" runat="server" AutoGenerateColumns="false" 

          EmptyDataText="No users belong to this role."> 
          <Columns> 
               <asp:TemplateField HeaderText="Users"> 
                    <ItemTemplate> 
                         <asp:Label runat="server" id="UserNameLabel" 
                              Text='<%# Container.DataItem %>'></asp:Label> 

                    </ItemTemplate> 
               </asp:TemplateField> 
          </Columns> 
     </asp:GridView> </p>

RoleList DropDownList にシステム内の一連のロールを移入する必要があります。 これを実現するには、BindRolesToList メソッドを更新して、Roles.GetAllRoles メソッドで返された文字列配列を RolesList DropDownList (および UsersRoleList Repeater) にバインドするようにします。

private void BindRolesToList() 
{ 
     // Get all of the roles 

     string[] roles = Roles.GetAllRoles(); 
     UsersRoleList.DataSource = roles; 
     UsersRoleList.DataBind(); 

     RoleList.DataSource = roles; 
     RoleList.DataBind(); 
}

BindRolesToList メソッド内の最後 2 行が追加されて一連のロールが RoleList DropDownList コントロールにバインドされました。 図 5 に、ブラウザーで表示したときの最終的な結果を示します。ドロップダウン リストにシステムのロールが移入されています。

The Roles are Displayed in the RoleList DropDownList

図 5: ロールが RoleList DropDownList に表示される (クリックするとフルサイズの画像が表示されます)

選択したロールに属しているユーザーを表示する

ページが最初に読み込まれるとき、または RoleList DropDownList から新しいロールが選択されたときに、そのロールに属するユーザーのリストを GridView に表示する必要があります。 次のコードを使って DisplayUsersBelongingToRole という名前のメソッドを作成します:

private void DisplayUsersBelongingToRole() 
{ 
     // Get the selected role 
     string selectedRoleName = RoleList.SelectedValue; 

     // Get the list of usernames that belong to the role 
     string[] usersBelongingToRole = Roles.GetUsersInRole(selectedRoleName); 

     // Bind the list of users to the GridView 
     RolesUserList.DataSource = usersBelongingToRole; 
     RolesUserList.DataBind(); 
}

このメソッドでは、最初に、RoleList DropDownList から、選択されているロールが取得されます。 次に、Roles.GetUsersInRole(roleName) メソッドを使って、そのロールに属するユーザーの UserName の文字列配列が取得されます。 その後、この配列が RolesUserList GridView にバインドされます。

このメソッドは次の 2 つの状況で呼び出す必要があります。ページが最初に読み込まれるときと、RoleList DropDownList での、選択されているロールが変更されたときです。 そのため、Page_Load イベント ハンドラーを更新して、CheckRolesForSelectedUser の呼び出しの後にこのメソッドが呼び出されるようにします。 次に、RoleListSelectedIndexChanged イベントのイベント ハンドラーを作成し、そこからもこのメソッドを呼び出します。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 

          // Check the selected user's roles 
          CheckRolesForSelectedUser(); 

          // Display those users belonging to the currently selected role 
          DisplayUsersBelongingToRole(); 
     } 
} 

... 

protected void RoleList_SelectedIndexChanged(object sender, EventArgs e) 
{ 
     DisplayUsersBelongingToRole(); 
}

このコードを配置すると、RolesUserList GridView に、選択したロールに属しているユーザーが表示されるようになります。 図 6 に示すように、Supervisors ロールは、Bruce と Tito という 2 人のメンバーから成ります。

The GridView Lists Those Users That Belong to the Selected Role

図 6: 選択したロールに属しているユーザー がGridView で一覧表示される (クリックするとフルサイズの画像が表示されます)

選択したロールからユーザーを削除する

[Remove] (削除) ボタンの列が含まれるように RolesUserList GridView を拡張してみましょう。 特定のユーザーの [Remove] (削除) ボタンをクリックすると、そのユーザーがそのロールから削除されます。

まず、GridView に Delete ボタン フィールドを追加します。 このフィールドが最も左のフィールドとして表示されるようにし、その DeleteText プロパティを "Delete" (既定値) から "Remove" に変更します。

Screenshot that shows how to add the “Remove” button in the Fields window.

図 7: GridView に [Remove] (削除) ボタンを追加する (クリックするとフルサイズの画像が表示されます)

[Remove] (削除) ボタンがクリックされると、ポストバックが発生し、GridView の RowDeleting イベントが発生します。 このイベントのイベント ハンドラーを作成し、選択されているロールからユーザーを削除するコードを記述する必要があります。 イベント ハンドラーを作成してから、次のコードを追加します:

protected void RolesUserList_RowDeleting(object sender, GridViewDeleteEventArgs e) 
{ 
     // Get the selected role 
     string selectedRoleName = RoleList.SelectedValue; 

     // Reference the UserNameLabel 
     Label UserNameLabel = RolesUserList.Rows[e.RowIndex].FindControl("UserNameLabel") as Label; 

     // Remove the user from the role 
     Roles.RemoveUserFromRole(UserNameLabel.Text, selectedRoleName); 

     // Refresh the GridView 
     DisplayUsersBelongingToRole(); 

     // Display a status message 
     ActionStatus.Text = string.Format("User {0} was removed from role {1}.", UserNameLabel.Text, selectedRoleName); 
}

このコードでは、最初に、選択したロールの名前が特定されます。 次に、削除するユーザーの UserName を特定するために、プログラムによって、[Remove] (削除) ボタンがクリックされた行から UserNameLabel コントロールが参照されます。 次は、Roles.RemoveUserFromRole メソッドの呼び出しによってそのユーザーがそのロールから削除されます。 その後、RolesUserList GridView が更新され、ActionStatus Label コントロールでメッセージが表示されます。

Note

この [Remove] (削除) ボタンでは、ロールからユーザーを削除する前にユーザーに確認を求めることはありません。 何らかのユーザー確認を追加するようお勧めします。 アクションを確認するための最も簡単な方法の 1 つは、クライアント側の確認ダイアログ ボックスの使用です。 この手法について詳しくは、「削除時、クライアント側の確認を追加する」をご覧ください。

図 8 に、ユーザー Tito が Supervisors グループから削除された後のページを示します。

Alas, Tito is No Longer a Supervisor

図 8: Tito はスーパーバイザではなくなった (クリックするとフルサイズの画像が表示されます)

選択したロールに新しいユーザーを追加する

このページの訪問者は、選択したロールからユーザーを削除するだけでなく、選択したロールにユーザーを追加することもできる必要があります。 選択したロールにユーザーを追加するための最適なインターフェイスは、想定されるユーザー アカウント数によって異なります。 Web サイトのユーザー アカウント数が数十以下になる予定の場合は、ここで DropDownList を使用できます。 ユーザー アカウント数が何千にもなる場合は、訪問者がアカウントのページング、特定のアカウントの検索、何らかの方法でのユーザー アカウントのフィルター処理をできるユーザー インターフェイスを含める必要があるでしょう。

このページでは、システム内のユーザー アカウントの数に関係なく動作する、単純なインターフェイスを使いましょう。 つまり、TextBox を使って、訪問者に、選択したロールに追加するユーザーのユーザー名を入力するよう求めます。 その名前のユーザーが存在しない場合や、そのユーザーが既にそのロールのメンバーである場合は、ActionStatus Label でメッセージを表示します。 しかし、ユーザーが存在し、そのロールのメンバーでない場合は、そのユーザーをそのロールに追加し、グリッドを更新します。

GridView の下に TextBox と Button を追加します。 TextBox の IDUserNameToAddToRole に設定し、Button の ID および Text プロパティをそれぞれ AddUserToRoleButton および "Add User to Role" (ユーザーをロールに追加) に設定します。

<p> 
     <b>UserName:</b> 
     <asp:TextBox ID="UserNameToAddToRole" runat="server"></asp:TextBox> 
     <br /> 
     <asp:Button ID="AddUserToRoleButton" runat="server" Text="Add User to Role" /> 

</p>

次に、AddUserToRoleButtonClick イベント ハンドラーを作成し、次のコードを追加します:

protected void AddUserToRoleButton_Click(object sender, EventArgs e) 
{ 
     // Get the selected role and username 

     string selectedRoleName = RoleList.SelectedValue; 
     string userNameToAddToRole = UserNameToAddToRole.Text; 

     // Make sure that a value was entered 
     if (userNameToAddToRole.Trim().Length == 0) 
     { 
          ActionStatus.Text = "You must enter a username in the textbox."; 
          return; 
     } 

     // Make sure that the user exists in the system 
     MembershipUser userInfo = Membership.GetUser(userNameToAddToRole); 
     if (userInfo == null) 
     { 
          ActionStatus.Text = string.Format("The user {0} does not exist in the system.", userNameToAddToRole); 

          return; 
     } 

     // Make sure that the user doesn't already belong to this role 
     if (Roles.IsUserInRole(userNameToAddToRole, selectedRoleName)) 
     { 
          ActionStatus.Text = string.Format("User {0} already is a member of role {1}.", userNameToAddToRole, selectedRoleName); 
          return; 
     } 

     // If we reach here, we need to add the user to the role 
     Roles.AddUserToRole(userNameToAddToRole, selectedRoleName); 

     // Clear out the TextBox 
     UserNameToAddToRole.Text = string.Empty; 

     // Refresh the GridView 
     DisplayUsersBelongingToRole(); 

     // Display a status message 

     ActionStatus.Text = string.Format("User {0} was added to role {1}.", userNameToAddToRole, selectedRoleName); }

Click イベント ハンドラー内のコードの大部分では、さまざまな検証チェックが実行されます。 これにより、訪問者が UserNameToAddToRole TextBox でユーザー名を指定してあり、そのユーザーがシステムに存在し、選択したロールにまだ属していないことが確認されます。 これらのチェックのいずれかに不合格だった場合は、適切なメッセージが ActionStatus に表示され、イベント ハンドラーが終了されます。 すべてのチェックに合格した場合は、Roles.AddUserToRole メソッドでそのユーザーがそのロールに追加されます。 その後、TextBox の Text プロパティがクリアされ、GridView が更新され、ActionStatus Label で、指定したユーザーが選択したロールに正常に追加されたことを示すメッセージが表示されます。

Note

指定されたユーザーが選択されたロールにまだ属していないことを確認するために、Roles.IsUserInRole(userName, roleName) メソッドを使います。これにより、userName が roleName のメンバーであるかどうかを示すブール値が返されます。 次のチュートリアルで、ロールベースの承認を確認するときに、このメソッドをもう一度使います。

ブラウザーでこのページにアクセスし、RoleList DropDownList から [Supervisors] ロールを選択します。 無効なユーザー名を入力してみてください。そのユーザーがシステムに存在しないことを示すメッセージが表示されます。

You Cannot Add a Non-Existent User to a Role

図 9: 存在しないユーザーはロールに追加できない (クリックするとフルサイズの画像が表示されます)

次に、有効なユーザーを追加してみてください。 先に進み、Tito を Supervisors ロールにもう一度追加します。

Tito Is Once Again a Supervisor!

図 10: Tito がもう一度スーパーバイザになった! (クリックするとフルサイズの画像が表示されます)

手順 3: "ユーザー別" および "ロール別" インターフェイスを相互に更新する

UsersAndRoles.aspx ページでは、ユーザーとロールを管理するための 2 つの異なるインターフェイスが用意されています。 現在、これら 2 つのインターフェイスは互いに独立して動作するため、一方のインターフェイスで行われた変更がもう一方にすぐに反映されない可能性があります。 たとえば、このページの訪問者が RoleList DropDownList から [Supervisors] ロールを選択するとします。それにより、そのメンバーとして [Bruce] と [Tito] が一覧表示されます。 次に、訪問者が UserList DropDownList から [Tito] を選択します。それにより、UsersRoleList Repeater 内の [Administrators] および [Supervisors] チェック ボックスが選択されます。 その後、訪問者が Repeater から [Supervisor] ロールの選択を解除すると、Tito が Supervisors ロールから削除されますが、この変更は "ロール別" インターフェイスには反映されません。 その GridView では、引き続き Tito が Supervisors ロールのメンバーとして表示されます。

これを修正するには、UsersRoleList Repeater からロールが選択されるか選択解除されるたびに、GridView を更新する必要があります。 同じように、"ロール別" インターフェイスからロールに対してユーザーが削除または追加されるたびに、Repeater を更新する必要があります。

"ユーザー別" インターフェイスでの Repeater は、CheckRolesForSelectedUser メソッドの呼び出しによって更新されます。 "ロール別" インターフェイスは、RolesUserList GridView の RowDeleting イベント ハンドラーと、AddUserToRoleButton Button の Click イベント ハンドラーで変更できます。 したがって、これらの各メソッドから CheckRolesForSelectedUser メソッドを 呼び出す必要があります。

protected void RolesUserList_RowDeleting(object sender, GridViewDeleteEventArgs e) 
{ 
     ... Code removed for brevity ... 

     // Refresh the "by user" interface 
     CheckRolesForSelectedUser(); 
} 

protected void AddUserToRoleButton_Click(object sender, EventArgs e) 
{ 
     ... Code removed for brevity ... 


     // Refresh the "by user" interface 
     CheckRolesForSelectedUser(); 
}

同様に、"ロール別" インターフェイスでの GridView は DisplayUsersBelongingToRole メソッドの呼び出しによって更新され、"ユーザー別" インターフェイスは RoleCheckBox_CheckChanged イベント ハンドラーを介して変更されます。 そのため、このイベント ハンドラーから DisplayUsersBelongingToRole メソッドを呼び出す必要があります。

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 
     ... Code removed for brevity... 

     // Refresh the "by role" interface 
     DisplayUsersBelongingToRole(); 
}

これらの小さなコード変更により、"ユーザー別" インターフェイスと "ロール別" インターフェイスが正しく相互に更新されるようになりました。 これを確認するには、ブラウザーでこのページにアクセスし、UserList および RoleList DropDownList からそれぞれ [Tito] と [Supervisors] を選択します。 "ユーザー別" インターフェイスで Repeater から [Tito] の [Supervisors] ロールの選択を解除すると "ロール別" インターフェイスで GridView から [Tito] が自動的に削除されることに注目してください。 "ロール別" インターフェイスで Supervisors ロールに Tito をもう一度追加すると "ユーザー別" インターフェイスで自動的に [Supervisors] チェック ボックスがもう一度選択されます。

手順 4: CreateUserWizard をカスタマイズして "Specify Roles" (ロールの指定) ステップを含める

ユーザー アカウントを作成する」チュートリアルでは、CreateUserWizard Web コントロールを使ってユーザー アカウントの新規作成のためのインターフェイスを提供する方法を説明しました。 CreateUserWizard コントロールは、次の 2 つのどちらかの手段として使うことができます:

  • 訪問者がサイトで自分用のユーザー アカウントを作成するための手段として
  • 管理者が新しいアカウントを作成するための手段として

1 つ目の使用事例では、訪問者がサイトにアクセスし、CreateUserWizard に入力して、サイトへの登録のために自分の情報を入力します。 2 つ目の事例では、管理者が別のユーザー用の新しいアカウントを作成します。

管理者が他のユーザー用のアカウントを作成するときには、新しいユーザー アカウントがどのロールに属するかを管理者が指定できると便利な場合があります。 「追加のユーザー情報を格納する」チュートリアルでは、WizardSteps をさらに追加して CreateUserWizard をカスタマイズする方法について説明しました。 新しいユーザーのロールを指定するためのステップをさらに CreateUserWizard に追加する方法を確認しましょう。

CreateUserWizardWithRoles.aspx ページを開き、RegisterUserWithRoles という名前の CreateUserWizard コントロールを追加します。 そのコントロールの ContinueDestinationPageUrl プロパティを "~/Default.aspx" に設定します。 ここでは、管理者がこの CreateUserWizard コントロールを使って新しいユーザー アカウントを作成するという想定であるため、そのコントロールの LoginCreatedUser プロパティを False に設定します。 この LoginCreatedUser プロパティでは、訪問者がユーザーを作成すると自動的にそのユーザーとしてログオンするようにするかどうかを指定します。その既定値は True です。 管理者が新しいアカウントを作成したときに自分自身としてサインインしたままにする必要があるため、これは False に設定します。

次に、CreateUserWizard のスマート タグから [WizardSteps の追加と削除…] を選択し、新しい WizardStep を追加して、その IDSpecifyRolesStep に設定します。 SpecifyRolesStep WizardStep を、[Sign Up for Your New Account] (新しいアカウントにサインアップ) ステップの後、[Complete] (完了) ステップの前に移動します。 WizardStepTitle プロパティを "Specify Roles" (ロールの指定) に、その StepType プロパティを Step に、その AllowReturn プロパティを False に設定します。

Screenshot that shows the selected Specify Roles properties in the Wizard Step Collection Editor window.

図 11: "Specify Roles" WizardStep を CreateUserWizard に 追加する (クリックするとフルサイズの画像が表示されます)

この変更の後は、CreateUserWizard の宣言型マークアップは次のようになります:

<asp:CreateUserWizard ID="RegisterUserWithRoles" runat="server" 
     ContinueDestinationPageUrl="~/Default.aspx" LoginCreatedUser="False"> 

     <WizardSteps> 
          <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server"> 
          </asp:CreateUserWizardStep> 
          <asp:WizardStep ID="SpecifyRolesStep" runat="server" StepType="Step" 

               Title="Specify Roles" AllowReturn="False"> 
          </asp:WizardStep> 
          <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server"> 
          </asp:CompleteWizardStep> 
     </WizardSteps> 

</asp:CreateUserWizard>

"Specify Roles" WizardStep で、RoleList という名前の CheckBoxList を追加します。 この CheckBoxList では、使用可能なロールが一覧表示されます。これにより、このページにアクセスするユーザーが、新規作成したユーザーが属するロールを選択できるようになります。

あと 2 つコーディング タスクが残っています。1 つ目では、RoleList CheckBoxList にシステム内のロールを移入する必要があります。2 つ目では、ユーザーが "Specify Roles" (ロールの指定) ステップから "Complete" (完了) ステップに移ったときに、作成されたユーザーを選択されたロールに追加する必要があります。 Page_Load イベント ハンドラーで 1 つ目のタスクを実現できます。 次のコードでは、このページへの初回訪問時にプログラムによって RoleList CheckBox が参照され、システム内のロールがそれにバインドされています。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Reference the SpecifyRolesStep WizardStep 
          WizardStep SpecifyRolesStep = RegisterUserWithRoles.FindControl("SpecifyRolesStep") as WizardStep; 

          // Reference the RoleList CheckBoxList 
          CheckBoxList RoleList = SpecifyRolesStep.FindControl("RoleList") as CheckBoxList; 

          // Bind the set of roles to RoleList 
          RoleList.DataSource = Roles.GetAllRoles(); 
          RoleList.DataBind(); 
     } 
}

上のコードには見覚えがあるはずです。 「追加のユーザー情報を格納する」チュートリアルでは、2 つの FindControl ステートメントを使ってカスタム WizardStep 内から Web コントロールを参照しました。 ロールを CheckBoxList にバインドするコードは、このチュートリアルの前半から引用しました。

2 つ目のプログラミング タスクを実行するには、"Specify Roles" (ロールの指定) ステップがいつ完了したかを知る必要があります。 CreateUserWizard には ActiveStepChanged イベントがあることを思い出してください。これは、訪問者があるステップから別のステップに移るたびに発生します。 ここでは、ユーザーが "Complete" (完了) ステップに達したかどうかを特定できます。達した場合は、選択されたロールにユーザーを追加する必要があります。

ActiveStepChanged イベントのイベント ハンドラーを作成し、次のコードを追加します:

protected void RegisterUserWithRoles_ActiveStepChanged(object sender, EventArgs e) 
{ 
     // Have we JUST reached the Complete step? 
     if (RegisterUserWithRoles.ActiveStep.Title == "Complete") 
     { 
          // Reference the SpecifyRolesStep WizardStep 
          WizardStep SpecifyRolesStep = RegisterUserWithRoles.FindControl("SpecifyRolesStep") as WizardStep; 

          // Reference the RoleList CheckBoxList 
          CheckBoxList RoleList = SpecifyRolesStep.FindControl("RoleList") as CheckBoxList; 

          // Add the checked roles to the just-added user 
          foreach (ListItem li in RoleList.Items) 

          { 
               if (li.Selected) 
                    Roles.AddUserToRole(RegisterUserWithRoles.UserName, li.Text); 
          } 
     } 
}

ユーザーが "Completed" (完了) ステップに達したら、そのイベント ハンドラーで RoleList CheckBoxList の項目が列挙され、先ほど作成したユーザーが、選択したロールに割り当てられます。

ブラウザーでこのページにアクセスします。 CreateUserWizard での最初のステップは、標準の "Sign Up for Your New Account" (新しいアカウントへのサインアップ) ステップであり、これは、新しいユーザーのユーザー名、パスワード、メール、主要なその他の情報の入力を求めます。 Wanda という名前の新しいユーザーを作成するための情報を入力します。

Create a New User Named Wanda

図 12: Wanda という名前の新しいユーザーを作成する (クリックするとフルサイズの画像が表示されます)

[Create User] (ユーザーの作成) をクリックします。 CreateUserWizard で Membership.CreateUser メソッドが内部的に呼び出されて新しいユーザー アカウントが作成されてから、次のステップ "Specify Roles" (ロールの指定) に進みます。ここでは、システム ロールが一覧表示されます。 [Supervisors] (スーパーバイザ) チェック ボックスを選択し、[Next] (次へ) をクリックします。

Make Wanda a Member of the Supervisors Role

図 13: Wanda を Supervisors ロールのメンバーにする (クリックするとフルサイズの画像が表示されます)

[Next] (次へ) をクリックするとポストバックが発生し、ActiveStep が "Complete" (完了) ステップに更新されます。 ActiveStepChanged イベント ハンドラーで、最近作成したユーザー アカウントが Supervisors ロールに割り当てられます。 これを確認するには、UsersAndRoles.aspx ページに戻り、RoleList DropDownList から [Supervisors] (スーパーバイザ) を選択します。 図 14 に示すように、スーパーバイザは現在、3 人のユーザー (Bruce、Tito、Wanda) で構成されています。

Bruce, Tito, and Wanda are All Supervisors

図 14: Bruce、Tito、Wanda は全員スーパーバイザである (クリックするとフルサイズの画像が表示されます)

まとめ

Roles フレームワークでは、特定のユーザーのロールに関する情報を取得するためのメソッドと、指定したロールに属しているユーザーを特定するためのメソッドが用意されています。 さらに、1 つ以上のロールに対して 1 人以上のユーザーを追加および削除するメソッドも多数あります。 このチュートリアルでは、これらのメソッドのうち、AddUserToRoleRemoveUserFromRole という 2 つのみに焦点を当てました。 他にも種類があり、それらは複数のユーザーを 1 つのロールに追加するためや複数のロールを 1 人のユーザーに割り当てるために設計されています。

このチュートリアルでは、新規作成したユーザーのロールを指定するための WizardStep を含めるように CreateUserWizard コントロールを拡張する方法も説明しました。 このようなステップは、新しいユーザー用のユーザー アカウントを作成するプロセスを管理者が効率化するのに役立ちます。

この時点では、ロールを作成および削除する方法と、ロールに対してユーザーを追加および削除する方法は説明してあります。 しかし、ロールベースの承認の適用についてはまだ説明していません。 次のチュートリアルでは、ロールごとに URL 承認規則を定義する方法と、現在ログインしているユーザーのロールに基づいてページレベルの機能を制限する方法について説明します。

プログラミングに満足!

もっと読む

この記事で説明したトピックの詳細については、次のリソースを参照してください。

作成者について

複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell 氏は、1998 年から Microsoft Web のテクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 Scott には、mitchell@4guysfromrolla.com または http://ScottOnWriting.NET のブログを介して連絡できます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Teresa Murphy でした。 今後の MSDN の記事を確認することに関心がありますか? ご希望の場合は、mitchell@4GuysFromRolla.com までお知らせください