Преамбула: это немного философский вопрос. Я больше ищу "правильный" способ сделать это, а не "способ" сделать это.
Предположим, что у меня есть некоторые продукты, и приложение ASP.NET MVC, выполняющее CRUD для этих продуктов: -
mysite.example/products/1
mysite.example/products/1/edit
Я использую шаблон репозитория, поэтому не имеет значения, откуда эти продукты: -
public interface IProductRepository
{
IEnumberable<Product> GetProducts();
....
}
Также мой репозиторий описывает список пользователей и какие продукты они являются менеджерами для (многие-многие между пользователями и продуктами). В другом месте приложения Super-Admin выполняет CRUD для пользователей и управляет отношениями между Пользователями и Продуктами, которым им разрешено управлять.
Любому разрешено просматривать любой продукт, но только пользователям, которые обозначены как "админы" для определенного продукта, разрешено вызывать, например, действие "Изменить".
Как должен работать с ASP.NET MVC? Если я что-то пропустил, я не могу использовать встроенный атрибут авторизации ASP.NET, поскольку сначала мне понадобилась бы другая роль для каждого продукта, а во-вторых, я не буду знать, какую роль проверить, пока не найду извлек мой продукт из репозитория.
Очевидно, вы можете обобщить этот сценарий на большинство сценариев управления контентом - например, Пользователям разрешено редактировать собственные сообщения Форума. Пользователям StackOverflow разрешено редактировать собственные вопросы - если у них нет 2000 или более сообщений...
Простейшим решением, например, было бы что-то вроде: -
public class ProductsController
{
public ActionResult Edit(int id)
{
Product p = ProductRepository.GetProductById(id);
User u = UserService.GetUser(); // Gets the currently logged in user
if (ProductAdminService.UserIsAdminForProduct(u, p))
{
return View(p);
}
else
{
return RedirectToAction("AccessDenied");
}
}
}
Мои проблемы:
- Некоторый из этого кода нужно будет повторить - представьте, что в зависимости от отношения User-Products существует несколько операций (Update, Delete, SetStock, Order, CreateOffer). Вам придется копировать-вставлять несколько раз.
- Это не очень легко проверить - вы должны макетировать моим счетом четыре объекта для каждого теста.
- На самом деле не похоже, что "задание" контроллера проверяет, разрешено ли пользователю выполнять действие. Я бы предпочел более гибкое (например, AOP через атрибуты) решение. Однако, это обязательно означает, что вам придется дважды выбрать продукт (один раз в AuthorizationFilter и снова в контроллере)?
- Было бы лучше вернуть 403, если пользователю не разрешено делать этот запрос? Если да, то как мне это сделать?
Я, вероятно, продолжу это обновление, поскольку сам получаю идеи, но я очень хочу услышать ваше!
Спасибо заранее!
Edit
Просто добавьте немного деталей здесь. Проблема, с которой я сталкиваюсь, заключается в том, что я хочу, чтобы бизнес-правило "Только пользователи с разрешением могут редактировать продукты", которые должны содержаться в одном и только одном месте. Я чувствую, что тот же код, который определяет, может ли пользователь выполнить GET или POST для действия "Редактировать", также должен нести ответственность за определение того, нужно ли отображать ссылку "Изменить" в представлениях "Индекс" или "Детали". Может быть, это невозможно/невозможно, но я чувствую, что это должно быть...
Изменить 2
Запуск щедрости на этом. Я получил хорошие и полезные ответы, но ничего, что мне было комфортно "принимать". Имейте в виду, что я ищу хороший чистый метод, чтобы бизнес-логика определяла, будет ли отображаться ссылка "Редактировать" на индексном представлении в том же месте, которое определяет, будет ли запрос к продуктам/редактировать/1 разрешено или нет. Я бы хотел, чтобы загрязнение в моем методе действия достигло абсолютного минимума. В идеале я ищу решение на основе атрибутов, но я согласен, что это невозможно.