挑戦する
識別子、名前、TINを含む顧客モデルがあるとします。
public class ClientViewModel { public int Id { get; set; } public string Name { get; set; } public string Inn { get; set; } }
データはモデルの形式でページに届きます。 必要なアクションを完了すると、ユーザーはパラメーターとしてフォームフィールド値を含むHTTPリクエストを送信します。 要求の処理を行うActionメソッドが呼び出される前に、DefaultModelBinderはフォームから受け取ったパラメーターをデータモデルに関連付けます。 ユーザーが行ったすべての変更は、結果のモデルのプロパティに表示されます。

しかし、誤ってまたは悪意を持ってそうする権利を持たないユーザーがデータの整合性を侵害できないように、一部のプロパティを変更から保護する必要がある場合はどうでしょうか?! この問題を解決するには、モデルの一部のプロパティを特定のユーザーロールに変更できないようにするメカニズムを開発する必要があります。
ソリューションのアイデア
- 新しい属性を作成します。 その中で、編集が禁止されている役割を示します。 この属性は、モデルの特定のプロパティをマークします。
- ユーザーが属性で指定されたロールの少なくとも1つに属している場合、禁止されたプロパティのバインドが無視されるように、DefaultModelBinderのデフォルトモデルバインドをオーバーライドします。
解決策
属性を作成します。 Roles行には、禁止されているロールがカンマで区切られて表示されます。
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class DenyWriteRolesAttribute : Attribute { public string Roles { get; set; } }
新しいモデルバインディングメカニズムは、標準のDefaultModelBinderクラスから継承することにより実装されます。 特定のプロパティをバインドするBindPropertyメソッドをオーバーライドする必要があります。 プロパティにDenyWriteRoles属性があり、少なくとも1つのユーザーロールが属性で指定されたロールのいずれかに一致する場合、バインドは発生しません。
public class CustomModelBinder : DefaultModelBinder { protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) { var a = propertyDescriptor.Attributes.OfType<DenyWriteRolesAttribute>().FirstOrDefault(); if (a != null && a.Roles.Split(',').Any(r => controllerContext.HttpContext.User.IsInRole(r))) return; base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } }
以下は、CustomModelBinderをGlobal.asax.csファイルのデフォルトモデルバインディングとして設定する例です。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); ModelBinders.Binders.DefaultBinder = new CustomModelBinder(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }
ここで、「user」ロールを持つユーザーが「Name」モデルプロパティを変更できないようにするには、DenyWriteRoles属性を設定するだけで十分です。
public class ClientViewModel { public int Id { get; set; } [DenyWriteRoles(Roles = "user")] public string Name { get; set; } public string Inn { get; set; } }
結果
アプリケーションで作業する場合、ユーザーはモデルのすべてのプロパティを表示できますが、保護されたプロパティに関する変更は無視されます。
