1

ASP.NET MVC の AuthorizeAttribute を拡張して、ロール メンバーシップまたは問題のデータの「所有権」に基づくユーザーの承認の概念をサポートするようにしたいと思います。データ アクセスには LINQ2SQL を使用しています。asp.net mvc authentication using rolesにも同様の質問があります。

私が考えているのは、拡張された AuthorizeAttribute クラスに EntityProperty、UserProperty、RouteParameter、および JoinTableType パラメーターを追加することです。最初の 2 つは、チェックする結合テーブル内のプロパティの名前です。RouteParameter は、一致する EntityProperty の値を抽出するルート パラメーターの名前になります。現在のユーザー名を使用して、ユーザーのテーブルからユーザー ID を取得します。JoinTableType パラメーターは、ルート パラメーター値とユーザー ID が一致する必要がある Entity と UserProperties を含むデータ コンテキスト内のテーブルの型になります。

基本的な考え方は、擬似コードで次のとおりです。

 if authorizecore result is true
    user is granted access based on role
 else if user is not authenticated
    redirect to logon
 else if user is related to request
    user is granted access based on relation
 else
    user is not authorized, redirect to not authorized error view

is related テストは次のようになります。

 result = false
 find the matching user from user name
 find the entity property value in route data
 if user exists and entity property value exists
    get table from context matching join table type
    if table exists
       find row in table matching user id and entity property value
       if row exists
          result = true
       endif
    endif
 endif


 return result

私の質問は、LINQ クエリを作成する際に型とプロパティの名前をどのように使用すればよいかということです。それとも、これをすべてobjectリフレクションで行う必要がありますか。これを簡単にする方法についてのアイデアを本当に探しているので、他の提案もいただければ幸いです。チェックをアクションに直接埋め込むよりも、属性を使用して、他のアクションの処理方法と一貫性を保つことをお勧めします。

4

1 に答える 1

1

VS2008 サンプルの Dynamic Linq 拡張機能を使用して、かなり合理的な方法でこれを行うことができました。上記の 2 番目の擬似コード サンプルを表すコードを次に示します。最初の単体テストには合格しましたが、より堅牢にする必要があります。

使用法:

[RoleOrMemberAuthorization( UserTable = "Participants",
                            UserNameProperty = "UserName",
                            UserSelectionProperty = "ParticipantID",
                            JoinTable = "GroupLeaders",
                            EntityProperty = "GroupID",
                            UserEntityProperty = "ParticipantID",
                            RouteParameter = "id",
                            Roles = "SuperUser, ViewGroups" )]

次のように呼ばれます:

else if (IsRelated( filterContext,
                    this.GetTable( dc, this.JoinTable ), 
                    this.GetTable( dc, this.UserTable ) ))
{
    SetCachePolicy( filterContext );
}

関連ソース:

protected bool IsRelated( AuthorizationContext filterContext,
                          IQueryable joinTable,
                          IQueryable userTable )
{
    bool result = false;
    try
    {
        object entityIdentifier = filterContext.RouteData
                                               .Values[this.RouteParameter];
        object userIdentifier = this.GetUserIdentifer( filterContext, userTable );
        if (userIdentifier != null && entityIdentifier != null)
        {
            result = joinTable.Where( this.EntityProperty + "=@0 and "
                                      + this.UserEntityProperty + "=@1",
                                      entityIdentifier,
                                      userIdentifier )
                              .Count() > 0;
        }
    }
    catch (NullReferenceException) { }
    return result;
}

private object GetUserIdentifer( AuthorizationContext filterContext,
                                 IQueryable userTable )
{
    string userName = filterContext.HttpContext.User.Identity.Name;

    var query = userTable.Where( this.UserNameProperty + "=@0", userName )
                         .Select( this.UserSelectionProperty );

    object userIdentifer = null;
    foreach (var value in query)
    {
        userIdentifer = value;
        break;
    }
    return userIdentifer;
}

private IQueryable GetTable( DataContext context, string name )
{
    PropertyInfo info = context.GetType().GetProperty( name );
    if (info != null)
    {
        return info.GetValue( context, null ) as IQueryable;
    }
    else
    {
        return null;
    }
}
于 2009-01-15T20:06:01.443 に答える