ユーザーにロールを割り当てる (VB)
Note
この記事が作成されてから、ASP.NET メンバーシップ プロバイダーは ASP.NET Identity に置き換えられました。 この記事の作成時点で取り上げたメンバーシップ プロバイダーではなく、ASP.NET Identity プラットフォームを使用するようにアプリを更新することを強くお勧めします。 ASP.NET メンバーシップ システムと比べると、ASP.NET Identity には次のような多くの利点があります。
- パフォーマンスの向上
- 拡張性とテストの容易性の向上
- OAuth、OpenID Connect、2 要素認証のサポート
- クレームベースの ID のサポート
- ASP.Net Core との相互運用性の向上
このチュートリアルでは、どのユーザーがどのロールに属するかの管理に役立つ 2 つの ASP.NET ページを構築します。 1 つ目のページに含まれる機能は、特定のロールに属するユーザーの表示、特定のユーザーが属するロールの表示、および特定のロールに対する特定のユーザーの割り当てや削除です。 2 つ目のページでは、CreateUserWizard コントロールを拡張して、新しく作成されたユーザーが属するロールを指定する手順を含めます。 これは、管理者が新しいユーザー アカウントを作成できるシナリオにおいて役立ちます。
はじめに
前のチュートリアルでは、ロール フレームワークと SqlRoleProvider
について確認しました。Roles
クラスを使用してロールを作成、取得、削除する方法について説明しました。 ロールの作成と削除に加え、ロールに対してユーザーを割り当てまたは削除できる必要があります。 残念ながら、ASP.NET には、どのユーザーがどのロールに属するかを管理するための Web コントロールは付属していません。 代わりに、これらの関連付けを管理するための独自の ASP.NET ページを作成する必要があります。 幸い、ロールに対するユーザーの追加と削除はとても簡単です。 Roles
クラスには、1 人以上のユーザーを 1 つ以上のロールに追加するためのメソッドがいくつか含まれています。
このチュートリアルでは、どのユーザーがどのロールに属するかの管理に役立つ 2 つの ASP.NET ページを構築します。 1 つ目のページに含まれる機能は、特定のロールに属するユーザーの表示、特定のユーザーが属するロールの表示、および特定のロールに対する特定のユーザーの割り当てや削除です。 2 つ目のページでは、CreateUserWizard コントロールを拡張して、新しく作成されたユーザーが属するロールを指定する手順を含めます。 これは、管理者が新しいユーザー アカウントを作成できるシナリオにおいて役立ちます。
それでは始めましょう。
どのユーザーがどのロールに属しているかの一覧表示
このチュートリアルで最初に実行する必要がある作業は、ユーザーをロールに割り当てることができる Web ページを作成することです。 ユーザーをロールに割り当てる方法に注目する前に、まず、どのユーザーがどのロールに属しているかを判断する方法に集中しましょう。 この情報の表示には、"ロール別" または "ユーザー別" という 2 つの方法があります。訪問者にロールの選択を可能にしてそのロールに属するすべてのユーザーが表示されるようにするか ("ロール別" 表示)、訪問者にユーザーの選択を求めてそのユーザーに割り当てられているロールが表示されるようにする ("ユーザー別" 表示) ことができます。
"ロール別" 表示は、訪問者が、特定のロールに属する一連のユーザーを知りたい場合に役立ちます。"ユーザー別" 表示は、訪問者が、特定のユーザーのロールを知る必要がある場合に最適です。 ページに "ロール別" および "ユーザー別" インターフェイスを両方含めてみましょう。
まず、"ユーザー別" インターフェイスの作成を開始します。 このインターフェイスは、ドロップダウン リストとチェック ボックスのリストで構成されています。 ドロップダウン リストに、システム内の一連のユーザーが設定されます。チェックボックスで、それらのロールが列挙されます。 ドロップダウン リストからユーザーを選択すると、そのユーザーが属しているロールが確認されます。 このページにアクセスしたユーザーは、その後、チェックボックスを選択するか選択解除して、選択したユーザーを、対応するロールに対して追加または削除できます。
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 デザイナーでのこの効果を示します。
図 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 Sub BindUsersToUserList()
' Get all of the user accounts
Dim users As MembershipUserCollection = Membership.GetAllUsers()
UserList.DataSource = users
UserList.DataBind()
End Sub
Private Sub BindRolesToList()
' Get all of the roles
Dim roleNames() As String = Roles.GetAllRoles()
UsersRoleList.DataSource = roleNames
UsersRoleList.DataBind()
End Sub
BindUsersToUserList
メソッドでは、Membership.GetAllUsers
メソッド を使用してシステム内のすべてのユーザー アカウントが取得されます。 これにより、MembershipUser
インスタンスのコレクションである、MembershipUserCollection
オブジェクトが返されます。 その後、このコレクションが UserList
DropDownList にバインドされます。 このコレクションを構成する MembershipUser
インスタンスには、さまざまなプロパティが含まれています (UserName
、Email
、CreationDate
、IsOnline
など)。 UserName
プロパティの値を表示するよう DropDownList に指示するために、UserList
DropDownList の DataTextField
および DataValueField
プロパティが "UserName" に設定されていることを確認します。
Note
Membership.GetAllUsers
メソッドには、2 つのオーバーロードがあります。1 つは入力パラメーターを受け付けずすべてのユーザーを返し、もう 1 つはページ インデックスとページ サイズの整数値を受け取りユーザーの指定されたサブセットのみを返します。 ページング可能なユーザー インターフェイス要素に大量のユーザー アカウントが表示されている場合、この 2 番目のオーバーロードを使うと、すべてではなくユーザー アカウントの的確なサブセットのみが返されるため、より効率的にユーザーをページングできます。
BindRolesToList
メソッドでは、最初に、Roles
クラスの GetAllRoles
メソッド (システム内のロールを含む文字列配列を返す) が呼び出されます。 その後、この文字列配列は Repeater にバインドされます。
最終的に、これら 2 つのメソッドを、このページが最初に読み込まれるときに呼び出す必要があります。 Page_Load
イベント ハンドラーに次のコードを追加します。
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
' Bind the users and roles
BindUsersToUserList()
BindRolesToList()
End If
End Sub
このコードを配置したら、ブラウザーでこのページにアクセスします。画面は図 2 のようになります。 すべてのユーザー アカウントがドロップダウン リストに移入されており、その下に、各ロールがチェック ボックスとして表示されています。 DropDownList と CheckBox の AutoPostBack
プロパティを True に設定したため、選択したユーザーを変更するか、ロールを選択または選択解除すると、ポストバックが発生します。 しかし、これらのアクションに対処するコードをまだ記述していないため、何もアクションは実行されません。 次の 2 つの項で、これらのタスクに取り組みます。
図 2: ページにユーザーとロールが表示されている (クリックするとフルサイズの画像が表示されます)
選択したユーザーが属しているロールを確認する
ページが最初に読み込まれるとき、または訪問者がドロップダウン リストから新しいユーザーを選択するたびに、特定のロールのチェック ボックス (選択したユーザーが属しているロールのみ) が選択されるように UsersRoleList
チェック ボックスを更新する必要があります。 これを実現するには、次のコードを含む、CheckRolesForSelectedUser
という名前のメソッドを作成します:
Private Sub CheckRolesForSelectedUser()
' Determine what roles the selected user belongs to
Dim selectedUserName As String = UserList.SelectedValue
Dim selectedUsersRoles() As String = Roles.GetRolesForUser(selectedUserName)
' Loop through the Repeater's Items and check or uncheck the checkbox as needed
For Each ri As RepeaterItem In UsersRoleList.Items
' Programmatically reference the CheckBox
Dim RoleCheckBox As CheckBox = CType(ri.FindControl("RoleCheckBox"), CheckBox)
' See if RoleCheckBox.Text is in selectedUsersRoles
If Linq.Enumerable.Contains(Of String)(selectedUsersRoles, RoleCheckBox.Text) Then
RoleCheckBox.Checked = True
Else
RoleCheckBox.Checked = False
End If
Next
End Sub
上記のコードでは、最初に、選択したユーザーが誰であるかが特定されます。 その後、Roles クラスの GetRolesForUser(userName)
メソッドを使用して、指定したユーザーの一連のロールが文字列配列として返されます。 次に、Repeater の項目が列挙され、各項目の RoleCheckBox
CheckBox がプログラムによって参照されます。 この CheckBox は、その対応するロールが selectedUsersRoles
文字列配列に含まれている場合のみ選択されます。
Note
ASP.NET バージョン 2.0 を使用している場合、Linq.Enumerable.Contains(Of String)(...)
構文はコンパイルされません。 Contains(Of String)
メソッドは、LINQ ライブラリに含まれており、これは ASP.NET 3.5 の新機能です。 まだ ASP.NET バージョン 2.0 を使用している場合は、代わりに Array.IndexOf(Of String)
メソッドを使います。
CheckRolesForSelectedUser
メソッドは、次の 2 つの場合に呼び出す必要があります。ページが最初に読み込まれるときと、UserList
DropDownList の、選択されているインデックスが変更されるたびです。 したがって、Page_Load
イベント ハンドラーからこのメソッドを呼び出します (BindUsersToUserList
と BindRolesToList
の呼び出しの後)。 また、DropDownList の SelectedIndexChanged
イベントのイベント ハンドラーを作成し、そこからこのメソッドを呼び出します。
Protected Sub Page_Load(ByVal sender As Object,ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
' Bind the users and roles
BindUsersToUserList()
BindRolesToList()
' Check the selected user's roles
CheckRolesForSelectedUser()
End If
End Sub
...
Protected Sub UserList_SelectedIndexChanged(ByVal sender As Object,ByVal e As System.EventArgs) Handles UserList.SelectedIndexChanged
CheckRolesForSelectedUser()
End Sub
このコードを配置したら、ブラウザーでこのページをテストできます。 ただし、UsersAndRoles.aspx
ページには現在はユーザーをロールに割り当てる機能がないため、ロールがあるユーザーはいません。 ユーザーをロールに割り当てるためのインターフェイスはすぐに作成します。このコードが動作するという私の言葉を信じて後でそれを確認することも、この機能を今すぐテストするために aspnet_UsersInRoles
テーブルにレコードを挿入して手動でユーザーをロールに追加することもできます。
ロールに対してユーザーを割り当てるか削除する
訪問者が UsersRoleList
Repeater 内の CheckBox を選択または選択解除したときは、選択したユーザーを、対応するロールに対して追加または削除する必要があります。 CheckBox の AutoPostBack
プロパティは現在 True に設定されています。これにより、Repeater 内の CheckBox が選択または選択解除されるたびにポストバックが発生します。 要するに、CheckBox の CheckChanged
イベントのイベント ハンドラーを作成する必要があります。 この CheckBox は Repeater コントロールに含まれているため、イベント ハンドラーの仕組みを手動で追加する必要があります。 まず、次のように、分離コード クラスにそのイベント ハンドラーを Protected
メソッドとして追加します:
Protected Sub RoleCheckBox_CheckChanged(ByVal sender As Object, ByVal e As EventArgs)
End Sub
このイベント ハンドラーのコードの記述については後で説明します。 しかし、まず、イベント処理の仕組みを完成させましょう。 Repeater の ItemTemplate
内の CheckBox から、OnCheckedChanged="RoleCheckBox_CheckChanged"
を追加します。 この構文では、RoleCheckBox_CheckChanged
イベント ハンドラーを RoleCheckBox
の CheckedChanged
イベントに結び付けています。
<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 Sub RoleCheckBox_CheckChanged(ByVal sender As Object, ByVal e As EventArgs)
'Reference the CheckBox that raised this event
Dim RoleCheckBox As CheckBox = CType(sender, CheckBox)
' Get the currently selected user and role
Dim selectedUserName As String = UserList.SelectedValue
Dim roleName As String = RoleCheckBox.Text
' Determine if we need to add or remove the user from this role
If RoleCheckBox.Checked Then
' 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)
End If
End Sub
上記のコードでは、最初に、イベントの発生元の CheckBox (sender
入力パラメーターを介して使用可能) がプログラムによって参照されています。 CheckBox を選択した場合は、選択したユーザーが、指定したロールに追加されます。そうでない場合は、そのロールから削除されます。 どちらの場合も、ActionStatus
Label で、先ほど実行したアクションの概要のメッセージが表示されます。
ブラウザーでこのページをテストします。 ユーザー [Tito] を選択してから、Tito を Administrators および Supervisors ロールの両方に追加します。
図 3: Tito が Administrators および Supervisors ロールに追加された (クリックするとフルサイズの画像が表示されます)
次に、ドロップダウン リストからユーザー [Bruce] を選択します。 ポストバックがあり、Repeater の CheckBox が CheckRolesForSelectedUser
を介して更新されます。 Bruce はまだどのロールにも属していないため、2 つのチェック ボックスは選択されていません。 次は、Bruce を Supervisors ロールに追加します。
図 4: Bruce が Supervisors ロールに追加された (クリックするとフルサイズの画像が表示されます)
CheckRolesForSelectedUser
メソッドの機能をさらに確認するために、Tito や Bruce 以外のユーザーを選択します。 チェック ボックスの選択が自動的に解除されて、どのロールにも属さないことを意味していることに注目してください。 [Tito] に戻ります。 [Administrators] および [Supervisors] チェック ボックスが両方選択されています。
手順 2: "ロール別" ユーザー インターフェイスを作成する
この時点では、"ユーザー別" インターフェイスは完成しており、"ロール別" インターフェイスに取り組み始めるための準備ができています。 "ロール別" インターフェイスでは、ドロップダウン リストからロールを選択するようユーザーに求めるダイアログが表示され、そのロールに属する一連のユーザーが GridView に表示されます。
UsersAndRoles.aspx page
に別の DropDownList コントロールを追加します。 これを 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 Sub BindRolesToList()
' Get all of the roles
Dim roleNames() As String = Roles.GetAllRoles()
UsersRoleList.DataSource = roleNames
UsersRoleList.DataBind()
RoleList.DataSource = roleNames
RoleList.DataBind()
End Sub
BindRolesToList
メソッド内の最後 2 行が追加されて一連のロールが RoleList
DropDownList コントロールにバインドされました。 図 5 に、ブラウザーで表示したときの最終的な結果を示します。ドロップダウン リストにシステムのロールが移入されています。
図 5: ロールが RoleList
DropDownList に表示される (クリックするとフルサイズの画像が表示されます)
選択したロールに属しているユーザーを表示する
ページが最初に読み込まれるとき、または RoleList
DropDownList から新しいロールが選択されたときに、そのロールに属するユーザーのリストを GridView に表示する必要があります。 次のコードを使用して DisplayUsersBelongingToRole
という名前のメソッドを作成します:
Private Sub DisplayUsersBelongingToRole()
' Get the selected role
Dim selectedRoleName As String = RoleList.SelectedValue
' Get the list of usernames that belong to the role
Dim usersBelongingToRole() As String = Roles.GetUsersInRole(selectedRoleName)
' Bind the list of users to the GridView
RolesUserList.DataSource = usersBelongingToRole
RolesUserList.DataBind()
End Sub
このメソッドでは、最初に、RoleList
DropDownList から、選択されているロールが取得されます。 次に、Roles.GetUsersInRole(roleName)
メソッドを使用して、そのロールに属するユーザーの UserName の文字列配列が取得されます。 その後、この配列が RolesUserList
GridView にバインドされます。
このメソッドは次の 2 つの状況で呼び出す必要があります。ページが最初に読み込まれるときと、RoleList
DropDownList での、選択されているロールが変更されたときです。 そのため、Page_Load
イベント ハンドラーを更新して、CheckRolesForSelectedUser
の呼び出しの後にこのメソッドが呼び出されるようにします。 次に、RoleList
の SelectedIndexChanged
イベントのイベント ハンドラーを作成し、そこからもこのメソッドを呼び出します。
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
' Bind the users and roles
BindUsersToUserList()
BindRolesToList()
' Check the selected user's roles
CheckRolesForSelectedUser()
'Display those users belonging to the currently selected role
DisplayUsersBelongingToRole()
End If
End Sub
...
Protected Sub RoleList_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RoleList.SelectedIndexChanged
DisplayUsersBelongingToRole()
End Sub
このコードを配置すると、RolesUserList
GridView に、選択したロールに属しているユーザーが表示されるようになります。 図 6 に示すように、Supervisors ロールは、Bruce と Tito という 2 人のメンバーから成ります。
図 6: 選択したロールに属しているユーザー がGridView で一覧表示される (クリックするとフルサイズの画像が表示されます)
選択したロールからユーザーを削除する
[Remove] (削除) ボタンの列が含まれるように RolesUserList
GridView を拡張してみましょう。 特定のユーザーの [Remove] (削除) ボタンをクリックすると、そのユーザーがそのロールから削除されます。
まず、GridView に Delete ボタン フィールドを追加します。 このフィールドが最も左のフィールドとして表示されるようにし、その DeleteText
プロパティを "Delete" (既定値) から "Remove" に変更します。
図 7: GridView に [Remove] (削除) ボタンを追加する (クリックするとフルサイズの画像が表示されます)
[Remove] (削除) ボタンがクリックされると、ポストバックが発生し、GridView の RowDeleting
イベントが発生します。 このイベントのイベント ハンドラーを作成し、選択されているロールからユーザーを削除するコードを記述する必要があります。 イベント ハンドラーを作成してから、次のコードを追加します:
Protected Sub RolesUserList_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles RolesUserList.RowDeleting
' Get the selected role
Dim selectedRoleName As String = RoleList.SelectedValue
' Reference the UserNameLabel
Dim UserNameLabel As Label = CType(RolesUserList.Rows(e.RowIndex).FindControl("UserNameLabel"),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)
End Sub
このコードでは、最初に、選択したロールの名前が特定されます。 次に、削除するユーザーの UserName を特定するために、プログラムによって、[Remove] (削除) ボタンがクリックされた行から UserNameLabel
コントロールが参照されます。 次は、Roles.RemoveUserFromRole
メソッドの呼び出しによってそのユーザーがそのロールから削除されます。 その後、RolesUserList
GridView が更新され、ActionStatus
Label コントロールでメッセージが表示されます。
Note
この [Remove] (削除) ボタンでは、ロールからユーザーを削除する前にユーザーに確認を求めることはありません。 何らかのユーザー確認を追加するようお勧めします。 アクションを確認するための最も簡単な方法の 1 つは、クライアント側の確認ダイアログ ボックスの使用です。 この手法について詳しくは、「削除時、クライアント側の確認を追加する」をご覧ください。
図 8 に、ユーザー Tito が Supervisors グループから削除された後のページを示します。
図 8: Tito はスーパーバイザではなくなった (クリックするとフルサイズの画像が表示されます)
選択したロールに新しいユーザーを追加する
このページの訪問者は、選択したロールからユーザーを削除するだけでなく、選択したロールにユーザーを追加することもできる必要があります。 選択したロールにユーザーを追加するための最適なインターフェイスは、想定されるユーザー アカウント数によって異なります。 Web サイトのユーザー アカウント数が数十以下になる予定の場合は、ここで DropDownList を使用できます。 ユーザー アカウント数が何千にもなる場合は、訪問者がアカウントのページング、特定のアカウントの検索、何らかの方法でのユーザー アカウントのフィルター処理をできるユーザー インターフェイスを含める必要があるでしょう。
このページでは、システム内のユーザー アカウントの数に関係なく動作する、単純なインターフェイスを使いましょう。 つまり、TextBox を使用して、訪問者に、選択したロールに追加するユーザーのユーザー名を入力するよう求めます。 その名前のユーザーが存在しない場合や、そのユーザーが既にそのロールのメンバーである場合は、ActionStatus
Label でメッセージを表示します。 しかし、ユーザーが存在し、そのロールのメンバーでない場合は、そのユーザーをそのロールに追加し、グリッドを更新します。
GridView の下に TextBox と Button を追加します。 TextBox の ID
を UserNameToAddToRole
に設定し、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>
次に、AddUserToRoleButton
の Click
イベント ハンドラーを作成し、次のコードを追加します:
Protected Sub AddUserToRoleButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles AddUserToRoleButton.Click
' Get the selected role and username
Dim selectedRoleName As String = RoleList.SelectedValue
Dim userToAddToRole As String = UserNameToAddToRole.Text
' Make sure that a value was entered
If userToAddToRole.Trim().Length = 0 Then
ActionStatus.Text = "You must enter a username in the textbox."
Exit Sub
End If
' Make sure that the user exists in the system
Dim userInfo As MembershipUser = Membership.GetUser(userToAddToRole)
If userInfo Is Nothing Then
ActionStatus.Text = String.Format("The user {0} does not exist in the system.",userNameToAddToRole)
Exit Sub
End If
' Make sure that the user doesn't already belong to this role
If Roles.IsUserInRole(userToAddToRole, selectedRoleName) Then
ActionStatus.Text = String.Format("User {0} already is a member of role {1}.", UserNameToAddToRole,selectedRoleName)
Exit Sub
End If
' If we reach here, we need to add the user to the role
Roles.AddUserToRole(userToAddToRole, 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)
End Sub
Click
イベント ハンドラー内のコードの大部分では、さまざまな検証チェックが実行されます。 これにより、訪問者が UserNameToAddToRole
TextBox でユーザー名を指定してあり、そのユーザーがシステムに存在し、選択したロールにまだ属していないことが確認されます。 これらのチェックのいずれかに不合格だった場合は、適切なメッセージが ActionStatus
に表示され、イベント ハンドラーが終了されます。 すべてのチェックに合格した場合は、Roles.AddUserToRole
メソッドでそのユーザーがそのロールに追加されます。 その後、TextBox の Text
プロパティがクリアされ、GridView が更新され、ActionStatus
Label で、指定したユーザーが選択したロールに正常に追加されたことを示すメッセージが表示されます。
Note
指定されたユーザーが選択されたロールにまだ属していないことを確認するために、Roles.IsUserInRole(userName, roleName)
メソッドを使います。これにより、userName が roleName のメンバーであるかどうかを示すブール値が返されます。 次のチュートリアルで、ロールベースの承認を確認するときに、このメソッドをもう一度使います。
ブラウザーでこのページにアクセスし、RoleList
DropDownList から [Supervisors] ロールを選択します。 無効なユーザー名を入力してみてください。そのユーザーがシステムに存在しないことを示すメッセージが表示されます。
図 9: 存在しないユーザーはロールに追加できない (クリックするとフルサイズの画像が表示されます)
次に、有効なユーザーを追加してみてください。 先に進み、Tito を Supervisors ロールにもう一度追加します。
図 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 Sub RolesUserList_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles RolesUserList.RowDeleting
... Code removed for brevity ...
' Refresh the "by user" interface
CheckRolesForSelectedUser()
End Sub
Protected Sub AddUserToRoleButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles AddUserToRoleButton.Click
... Code removed for brevity ...
' Refresh the "by user" interface
CheckRolesForSelectedUser()
End Sub
同様に、"ロール別" インターフェイスでの GridView は DisplayUsersBelongingToRole
メソッドの呼び出しによって更新され、"ユーザー別" インターフェイスは RoleCheckBox_CheckChanged
イベント ハンドラーを介して変更されます。 そのため、このイベント ハンドラーから DisplayUsersBelongingToRole
メソッドを呼び出す必要があります。
Protected Sub RoleCheckBox_CheckChanged(ByVal sender As Object, ByVal e As EventArgs)
... Code removed for brevity ...
' Refresh the "by role" interface
DisplayUsersBelongingToRole()
End Sub
これらの小さなコード変更により、"ユーザー別" インターフェイスと "ロール別" インターフェイスが正しく相互に更新されるようになりました。 これを確認するには、ブラウザーでこのページにアクセスし、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
を追加して、その ID
を SpecifyRolesStep
に設定します。 SpecifyRolesStep WizardStep
を、[Sign Up for Your New Account] (新しいアカウントにサインアップ) ステップの後、[Complete] (完了) ステップの前に移動します。 WizardStep
の Title
プロパティを "Specify Roles" (ロールの指定) に、その StepType
プロパティを Step
に、その AllowReturn
プロパティを False に設定します。
図 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 Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
' Reference the SpecifyRolesStep WizardStep
Dim SpecifyRolesStep As WizardStep = CType(RegisterUserWithRoles.FindControl("SpecifyRolesStep"),WizardStep)
' Reference the RoleList CheckBoxList
Dim RoleList As CheckBoxList = CType(SpecifyRolesStep.FindControl("RoleList"), CheckBoxList)
' Bind the set of roles to RoleList
RoleList.DataSource = Roles.GetAllRoles()
RoleList.DataBind()
End If
End Sub
上のコードには見覚えがあるはずです。 「追加のユーザー情報を格納する」チュートリアルでは、2 つの FindControl
ステートメントを使用してカスタム WizardStep
内から Web コントロールを参照しました。 ロールを CheckBoxList にバインドするコードは、このチュートリアルの前半から引用しました。
2 つ目のプログラミング タスクを実行するには、"Specify Roles" (ロールの指定) ステップがいつ完了したかを知る必要があります。 CreateUserWizard には ActiveStepChanged
イベントがあることを思い出してください。これは、訪問者があるステップから別のステップに移るたびに発生します。 ここでは、ユーザーが "Complete" (完了) ステップに達したかどうかを特定できます。達した場合は、選択されたロールにユーザーを追加する必要があります。
ActiveStepChanged
イベントのイベント ハンドラーを作成し、次のコードを追加します:
Protected Sub RegisterUserWithRoles_ActiveStepChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RegisterUserWithRoles.ActiveStepChanged
'Have we JUST reached the Complete step?
If RegisterUserWithRoles.ActiveStep.Title = "Complete" Then
' Reference the SpecifyRolesStep WizardStep
Dim SpecifyRolesStep As WizardStep = CType(RegisterUserWithRoles.FindControl("SpecifyRolesStep"),WizardStep)
' Reference the RoleList CheckBoxList
Dim RoleList As CheckBoxList = CType(SpecifyRolesStep.FindControl("RoleList"), CheckBoxList)
' Add the checked roles to the just-added user
For Each li As ListItem In RoleList.Items
If li.Selected Then
Roles.AddUserToRole(RegisterUserWithRoles.UserName, li.Text)
End If
Next
End If
End Sub
ユーザーが "Completed" (完了) ステップに達したら、そのイベント ハンドラーで RoleList
CheckBoxList の項目が列挙され、先ほど作成したユーザーが、選択したロールに割り当てられます。
ブラウザーでこのページにアクセスします。 CreateUserWizard での最初のステップは、標準の "Sign Up for Your New Account" (新しいアカウントへのサインアップ) ステップであり、これは、新しいユーザーのユーザー名、パスワード、メール、主要なその他の情報の入力を求めます。 Wanda という名前の新しいユーザーを作成するための情報を入力します。
図 12: Wanda という名前の新しいユーザーを作成する (クリックするとフルサイズの画像が表示されます)
[Create User] (ユーザーの作成) をクリックします。 CreateUserWizard で Membership.CreateUser
メソッドが内部的に呼び出されて新しいユーザー アカウントが作成されてから、次のステップ "Specify Roles" (ロールの指定) に進みます。ここでは、システム ロールが一覧表示されます。 [Supervisors] (スーパーバイザ) チェック ボックスを選択し、[Next] (次へ) をクリックします。
図 13: Wanda を Supervisors ロールのメンバーにする (クリックするとフルサイズの画像が表示されます)
[Next] (次へ) をクリックするとポストバックが発生し、ActiveStep
が "Complete" (完了) ステップに更新されます。 ActiveStepChanged
イベント ハンドラーで、最近作成したユーザー アカウントが Supervisors ロールに割り当てられます。 これを確認するには、UsersAndRoles.aspx
ページに戻り、RoleList
DropDownList から [Supervisors] (スーパーバイザ) を選択します。 図 14 に示すように、スーパーバイザは現在、3 人のユーザー (Bruce、Tito、Wanda) で構成されています。
図 14: Bruce、Tito、Wanda は全員スーパーバイザである (クリックするとフルサイズの画像が表示されます)
まとめ
Roles フレームワークでは、特定のユーザーのロールに関する情報を取得するためのメソッドと、指定したロールに属しているユーザーを特定するためのメソッドが用意されています。 さらに、1 つ以上のロールに対して 1 人以上のユーザーを追加および削除するメソッドも多数あります。 このチュートリアルでは、これらのメソッドのうち、AddUserToRole
と RemoveUserFromRole
という 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 までお知らせください