我決定自定義模型粘合劑是要走的路。我已經可以禁用HTML控件了。但我需要有選擇地授權他們。
我知道該示例對象是人爲設計的 - 當然,您不會讓用戶發佈帶有兩個不可編輯屬性的對象 - 但重點是我不想讓用戶保留其值。我將NULL取出任何值,然後不更新任何NULL值。通過忽略NULL值,我不必去數據訪問來檢索當前值以替換違規更新。
此代碼有我對我的方式(使用MSPEC作爲測試框架):
public class TestSplitDetailViewModel
{
public int Id { get; set; }
[CanEdit]
public string RestrictedProperty { get; set; }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CanEditAttribute : Attribute
{
}
public class CanEditAttributeBinder : DefaultModelBinder
{
private readonly ISecurityTasks _securityTask;
private readonly ISecurityContext _securityContext;
public CanEditAttributeBinder(ISecurityTasks securityTask, ISecurityContext securityContext)
{
this._securityTask = securityTask;
this._securityContext = securityContext;
}
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
{
var canEditAttribute = propertyDescriptor.Attributes
.OfType<CanEditAttribute>()
.FirstOrDefault();
if (canEditAttribute != null)
{
bool allowed = IsAllowed();
if (allowed)
{
propertyDescriptor.SetValue(bindingContext.Model, null);
}
else
{
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
else
{
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
private bool IsAllowed()
{
return !this._securityTask.DoesUserHaveOperation(this._securityContext.User.Username, UserOperations.ReclassAllowed);
}
}
public class TestModelSpec : Specification<CanEditAttributeBinder>
{
protected static HomeController controller;
private static MockRepository mocks;
protected static ISecurityTasks securityTasks;
private static ISecurityContext securityContext;
protected static ModelBindingContext bindingContext;
Establish context =() =>
{
ServiceLocatorHelper.AddUserServiceWithTestUserContext();
securityTasks = DependencyOf<ISecurityTasks>().AddToServiceLocator();
securityContext = DependencyOf<ISecurityContext>().AddToServiceLocator();
user = new User("CHUNKYBACON");
securityContext.User = user;
// When we restricted access on the client,
// Chunky submitted a FORM POST in which he HACKED a value
var formCollection = new NameValueCollection
{
{ "TestSplitDetailViewModel.Id", "2" },
{ "TestSplitDetailViewModel.RestrictedProperty", "12" } // Given this is a hacked value
};
var valueProvider = new NameValueCollectionValueProvider(formCollection, null);
var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TestSplitDetailViewModel));
bindingContext = new ModelBindingContext
{
ModelName = "TestSplitDetailViewModel",
ValueProvider = valueProvider,
ModelMetadata = modelMetadata
};
controller = new HomeController(null, null, null, null, null);
mocks = new MockRepository();
MvcMockHelpers.SetFakeControllerContext(mocks, controller);
};
protected static User user;
protected static TestSplitDetailViewModel incomingModel;
}
public class when_a_restricted_user_changes_a_restricted_property : TestModelSpec
{
private Establish context =() => securityTasks.Stub(st =>
st.DoesUserHaveOperation(user.Username, UserOperations.ReclassAllowed)).Return(false);
Because of =() => incomingModel = (TestSplitDetailViewModel)subject.BindModel(controller.ControllerContext, bindingContext);
It should_null_that_value_out =() => incomingModel.RestrictedProperty.ShouldBeNull();
}
public class when_an_unrestricted_user_changes_a_restricted_property : TestModelSpec
{
private Establish context =() => securityTasks.Stub(st =>
st.DoesUserHaveOperation(user.Username, UserOperations.ReclassAllowed)).Return(true);
Because of =() => incomingModel = (TestSplitDetailViewModel)subject.BindModel(controller.ControllerContext, bindingContext);
It should_permit_the_change =() => incomingModel.RestrictedProperty.ShouldEqual("12");
}
編輯
現在這就是我的回答。我看到有些人可能會質疑我的測試DefaultModelBinder.BindProperty。我正在測試自定義覆蓋。
我會爲您的屬性建議一個自定義IsInRole驗證規則,如果規則失敗,它將忽略模型的更改。 –
@託德 - 感謝您的提示。沒有想過驗證規則。 模型綁定解決方案迫使我評估PropertyInfo是否存在我的目標屬性,然後才能檢查。在驗證規則中,我已經知道要執行檢查,因爲屬性本身會觸發它。將檢查出來並回來。 –
@託德 - 也沒有看到這一點。我需要檢查模型的值是否在持久性中相對於此實體發生了變化。如果我將屬性放在屬性上,則無法獲取父對象類型和ID,也無法檢索相應的數據訪問對象。我不希望這個屬性在課堂上進行裝飾。也許我需要更努力的谷歌類似的例子。 –