Стратегия запрета пользователям обновлять свойства ViewMode только для чтения с помощью роли / разрешения на сервере. - PullRequest
1 голос
/ 29 июля 2011

Платформа ASP.NET MVC 2.

У нас есть пользовательская история, которая гласит:

В [view] не разрешать пользователю редактировать [свойство], если пользователь не является [надлежащей ролью].Они все еще должны иметь возможность просматривать [свойство].

Итак, я должен показать поле для этих людей, просто запретить им изменять или обновлять значение свойства.

Я знаю, что могу разместить в представлении элемент управления только для чтения, используя атрибут для текущего пользователя. Это должно дать клиенту визуальную подсказку о том, что редактирование запрещено.Но стиль CSS не помешает кому-то взломать свой пост, чтобы изменить значение свойства.

Мой вопрос касается защиты свойства на стороне сервера.Какие методы я могу использовать для обнаружения изменений в моей модели входящего представления в этой ситуации - когда пользователь не может редактировать определенное свойство?

РЕДАКТИРОВАТЬ

Я бы хотелнужно держаться подальше от связок и белых списков - я ценю идеи!Они заставили меня осознать, что я пропустил ключевую информацию.

Мой владелец продукта хочет добавить свойства по своему усмотрению и по своему усмотрению - что я решил прочитать: нестатические решения не должны применяться.Кроме того, она хочет применить к своему приложению другую условную логику - «если состояние связанного свойства равно« X », то они могут редактировать независимо от разрешения» и т. Д. Я могу справиться с этой частью.Мне просто нужно знать, где их динамически применять.

Я думаю, что это специальное решение для связывания моделей.

Кстати, мы добавляем это конкретное разрешение к ролям:

var hasPermission = User.IsInRole(permission);

Ответы [ 3 ]

0 голосов
/ 30 июля 2011

Вы можете использовать атрибут Bind, который позволяет указывать свойства, которые должны быть включены или исключены при связывании. Вот хорошая базовая статья для получения дополнительной информации.

Исключить атрибуты, используя атрибут Bind

0 голосов
/ 30 июля 2011

Я решил, что подойдет пользовательская модель переплета.Я уже могу отключить элементы управления HTML.Но мне нужно выборочно авторизовать их.

Я знаю, что пример объекта придуман - конечно, вы бы не заставляли пользователей публиковать объект с двумя не редактируемыми свойствами - но дело в том, что я не хочучтобы позволить пользователю сохранить свою ценность.Я обнулю любое значение, а затем не буду обновлять пустые значения.Игнорируя значения 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.Я проверяю свое пользовательское переопределение.

0 голосов
/ 30 июля 2011

Я бы использовал белый или черный список и явно вызывал привязку модели к вашей модели. например,

[HttpPost]
public ActionResult Edit(int id) {
var item = db.GetByID(id); // get from DB
var whitelist = [ "Name", "Title", "Category", etc ]; // setup the list of fields you want to update

UpdateModel(item, whitelist);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...