だるろぐ

明日できることは、今日しない。

WebMatrix でユーザー認証機能(5) ―― ロール

f:id:daruyanagi:20120829200628p:plain

とりあえずログイン機能があらかた実装できたけれど、これだとだれでもユーザーが作れてしまう。まぁ、そういうアプリもあるけど、“管理者”のみがユーザーを作成できるほうが望ましいこともある。そういった“管理者”権限を実現したいときにはどうするかというと、“ロール(Roles)”を使うんだな。

System.Web.Security.Roles

ロールの作成方法については、ここらあたり(Adding Security and Membership to an ASP.NET Web Pages (Razor) Site | Microsoft Docs)が詳しい。

その例だとロールの作成するためにデータベースを直接イジっていて、一瞬「あちゃー、コードでやろうと思ったら SQL 書かなきゃいけないのかな?」と思ってしまったけど、ちゃんと Roles を扱うクラスが用意されていた*1

http://msdn.microsoft.com/en-us/library/system.web.security.roles.aspx

とりあえずさらっと目を通しておくとよさげ。

Roles.RoleExists() / Roles.CreateRole()

まず、 _AppStart.cshtml で“ロールがなければ作成する”という処理を追加してみた。

@{
    App.DATABASE = "database"; // <-- カッコ悪いし、
    App.TABLE_USERS = "Users"; //     あとで静的クラスにまとめて
    App.ROLE_ADMIN = "admin";  //     リードオンリーにしておくべき
    
    WebSecurity.InitializeDatabaseConnection(
        App.DATABASE, App.TABLE_USERS, "UserId", "Name", true);

    if (!Roles.RoleExists(App.ROLE_ADMIN)) // <-- ココ!
    {
        Roles.CreateRole(App.ROLE_ADMIN);
    }
}

f:id:daruyanagi:20120903091135p:plain

できたっぽい。拍子抜けしたぞなもし。

Roles.AddUserToRole()

つぎに、“初めて登録されたユーザーは管理者にする”ということにしてみようか。

# ~/Account/Register.cshtml

try
{
    WebSecurity.CreateUserAndAccount(
        name, password, new { Name = name });
    WebSecurity.Login(name, password);

    // 最初のユーザーには管理者権限を付与
    if (App.GetUserCount() <= 1)
    {
        Roles.AddUserToRole(name, App.ROLE_ADMIN);
    }

    Response.Redirect("~/");
}
catch (Exception e)
{
    ModelState.AddFormError(e.Message);
}

f:id:daruyanagi:20120903091712p:plain

ちゃんと UserId 1 に RoleId 1(admin)が紐付けられた。ちなみに、複数のユーザーを一度にロールへ突っ込むためのメソッドもある。

ちなみに、残念ながらユーザーの数を数えるメソッドは用意されていないみたいなので、これは SQL で書かないといけない。

@{
    App.GetUserCount = (Func<int>)GetUserCount; // <-- 個人的趣味
}

@functions
{
    int GetUserCount()
    {
        const string sql = "SELECT COUNT(*) FROM {0}";
        return Database
            .Open(App.DATABASE)
            .QueryValue(string.Format(sql, App.TABLE_USERS));
    }
}

でも、もしかしたらメソッドが用意されていない≒あんまりよくない実装 なのかもしれない。よく利用する処理なら、標準でメソッドが用意されているよね?*2 まぁ、それはまた今度考えよう。

Roles.IsUserInRole() / WebSecurity.RequireRoles()

んで、ロールが必要な処理には一行このように書き加えておく。この場合は、 Register.cshtml だね。 _PageStart.cshtml に書けば、フォルダ内の cshtml すべてに適用することもできる(はず、確かそうだった)。

@{
    WebSecurity.RequireRoles(App.ROLE_ADMIN);
}

f:id:daruyanagi:20120903091955p:plain

必要なロールをもたなければ、ログインページにリダイレクトされる*3ようだ*4

@{
    if (!Roles.IsUserInRole(App.ROLE_ADMIN))
    {
        :
        :

もちろん、ロールをもつかどうかの判定のみを行うこともできる。 寄り道: Rails の Flash っぽい機能を WebMatrix で使いたい - だるろぐ などを使ってユーザーにメッセージを通知したい場合など、いきなりリダイレクトされると困る場合はこっちを使ったほうがよさげだな。

*1:公式ドキュメントでデータベースを直接イジっていたのには、なにか理由があるのだろうか

*2:まぁ、案外そうとも限らないわけだけど

*3:今気付いたけど、 Log in to ってなんやねんw

*4:既定では ~/Account/LogIn みたい