Вопрос

Я создаю сайт сообщества в Rails для членов реальной организации.Я стараюсь придерживаться лучших практик RESTful design, и большая часть из них более или менее регламентирована.Проблема, которая заставляет мой мозг вращаться по четким спокойным кругам, - это проблема авторизации. Аутентификация это простая, давно решаемая проблема с широко распространенными решениями RESTful, но авторизация RESTful, похоже, является чем-то вроде черного искусства.Я пытаюсь найти подход, который обеспечит наиболее общую и гибкую структуру для управления доступом к ресурсам, будучи при этом максимально простым, и при этом соответствующим архитектуре RESTful.(А еще - пони.)

Соображения:

  1. Мне нужно контролировать доступ к различным ресурсам, таким как Пользователи, Страницы, Сообщения и так далее.
  2. Авторизация для данного ресурса должна быть более детальной, чем простой CRUD.
  3. Я хочу разрешить себе и другим редактировать правила авторизации из приложения.
  4. Правилам авторизации должно быть разрешено зависеть от предикатов, таких как (концептуально) Владелец (Пользователь, Ресурс) или Заблокированный (Тема)

Соображение (2) - это то, что беспокоит меня больше всего.Похоже, существует несоответствие импеданса между моей концепцией разрешений и концепцией действий RESTful.Например, возьмем Посты (как на доске объявлений).REST диктует наличие четырех операций над почтовым ресурсом:Создавайте, считывайте, обновляйте и удаляйте.Просто сказать, что пользователь должен иметь возможность обновлять свои собственные записи, но только определенным пользователям (или ролям, или группам) должно быть разрешено блокировать их.Традиционный способ представления блокировки находится в пределах состояния сообщения, но это приводит к тому, что Пользователь при тех же условиях может или не сможет обновить сообщение в зависимости от (полностью допустимых) значений, которые он предоставляет.Мне кажется очевидным, что на самом деле существуют два разных действия для изменения состояния Post, и включать их - значит просто маскировать нарушение принципов RESTful.

(Я должен отметить, что эта проблема сильно отличается от проблемы сбоя обновления из-за недействительный или непоследовательный данные—запрос на блокировку от непривилегированного пользователя в принципе вполне допустим, просто запрещен.)

Разве разложение - это не другое слово, обозначающее гниль?

Это можно преодолеть, разложив сообщение:блокировка - это вложенный ресурс определенной записи, и для ее создания или уничтожения у пользователя могут быть отдельные разрешения.В этом решении есть что-то общее, но оно сопряжено с как теоретическими, так и практическими трудностями.Если я исключу блокировки, то как насчет других атрибутов?Предположим, я в порыве каприза решу, что только члену Administrator должно быть разрешено изменять название поста?Простое изменение авторизации в этом случае потребовало бы реструктуризации базы данных для ее соответствия!Это не слишком удачное решение.Для обеспечения такого рода гибкости в рамках стратегии декомпозиции потребовалось бы, чтобы каждый атрибут был ресурсом.Это представляет собой своего рода дилемму.Мое неявное предположение заключалось в том, что ресурс представлен в базе данных в виде таблицы.Согласно этому предположению, ресурс для каждого атрибута означает таблицу для каждого атрибута.Очевидно, что это непрактично.Однако устранение этого предположения приводит к несоответствию импеданса между таблицами и ресурсами, что может привести к появлению собственных червей.Использование этого подхода потребовало бы гораздо более глубокого рассмотрения, чем я ему уделил.Во-первых, пользователи обоснованно ожидают, что смогут редактировать несколько атрибутов одновременно.Куда направляется запрос?К самому маленькому ресурсу, который содержит все атрибуты?К каждому отдельному ресурсу параллельно?На Луну?

Некоторые из этих вещей не похожи на другие…

Предположим тогда, что я не разлагаю атрибуты.Тогда альтернативой, по-видимому, является определение набора привилегий для каждого ресурса.На этом этапе, однако, однородность ПОКОЯ теряется.Чтобы определить правила доступа к ресурсу, система должна обладать конкретными знаниями о возможностях этого ресурса.Более того, теперь невозможно в общем виде распространять разрешения на дочерние ресурсы — даже если дочерний ресурс имел привилегию с тем же именем, между привилегиями нет внутренней семантической связи.Определение набора стандартных привилегий, подобного REST, кажется мне худшим из обоих миров, поэтому я застрял с отдельной иерархией разрешений для каждого типа ресурса.

Что ж, мы действительно сделали нос.И шляпа.Но это же ресурс!

Одно из предложений, которое я видел, которое смягчает некоторые недостатки вышеупомянутого подхода, заключается в определении разрешений как create / delete для Ресурсы и читать / записывать дальше атрибуты.Эта система представляет собой компромисс между атрибутами как ресурсами и привилегиями для каждого ресурса:в нем по-прежнему остается только CRUD, но для целей авторизации чтение и Обновление относятся к атрибутам, которые можно рассматривать как псевдоресурсы.Это обеспечивает многие практические преимущества подхода "атрибуты как ресурсы", хотя концептуальная целостность в определенной степени нарушена.Разрешения по-прежнему могут передаваться от ресурса к ресурсу и от ресурса к псевдоресурсу, но никогда от псевдоресурса.Я не до конца изучил последствия этой стратегии, но мне кажется, что она может быть многообещающей.Мне приходит в голову, что такая система лучше всего функционировала бы как неотъемлемая часть Модели.В Rails, например, это может быть модернизация ActiveRecord.Мне это кажется довольно радикальным, но авторизация является настолько фундаментальной сквозной проблемой, что это может быть оправдано.

Да, и не забудь про пони

Все это игнорирует проблему предикативных разрешений.Очевидно, что Пользователь должен иметь возможность редактировать свои собственные Сообщения, но ни чьи другие.Столь же очевидно, что таблица разрешений, написанная администратором, не должна содержать отдельных записей для каждого пользователя.Вряд ли это необычное требование — хитрость в том, чтобы сделать его универсальным.Я думаю, что всю необходимую мне функциональность можно было бы получить, сделав только Правила предикативный, чтобы вопрос о применимости правила мог быть решен быстро и незамедлительно.Правило "allow User write Post where Author(User, Post)" перевело бы на "for all User, Post such that Author(User, Post), allow User write Post", и "deny all write Post where Locked(Post)" кому "for all Post such that Locked(Post), deny all write Post".(Это было бы великий если бы все такие предикаты могли быть выражены в терминах одного пользователя и одного ресурса.) Концептуально результирующие "окончательные" правила были бы недирекативными.В связи с этим возникает вопрос о том, как внедрить такую систему.Предикаты должны быть членами классов моделей, но я не уверен, как можно было бы изящно ссылаться на них в контексте правил.Чтобы сделать это безопасно, потребовалось бы какое-то размышление.И здесь у меня снова возникает ощущение, что для этого потребуется доработка реализации Модели.

Повторите, как это пишется?

Последний вопрос заключается в том, как наилучшим образом представить эти правила авторизации в виде данных.Таблица базы данных могла бы сделать это с перечислением столбцов для allow / deny и C / R / U / D (или, возможно, CRUD-битов?или, может быть, {C, R, U, D} × {разрешить, запретить, наследовать}?), и столбец ресурсов с путем.Возможно, для удобства, бит "наследования".Я в растерянности относительно предикатов.Отдельный стол?Конечно, много кэширования, чтобы предотвратить его слишком безбожно медленно.


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

Это было полезно?

Решение

Извините, у меня нет времени должным образом ответить на этот вопрос, но приятно видеть несколько хорошо продуманных вопросов по SO.Вот несколько комментариев:

Не попадайтесь в ловушку сопоставления HTTP-глаголов с CRUD.Да, получить и УДАЛИТЬ карту довольно чисто, но PUT может создавать и обновлять (но только полную замену), а POST - это подстановочный глагол.POST действительно предназначен для обработки всего, что не вписывается в GET, PUT и Удалить.

Использование атрибутов для представления статуса объекта - это только один подход к управлению состоянием.Я предполагаю, что вы можете себе представить, что может сделать следующий запрос:

POST /LockedPosts?url=/Post/2010

Вспомогательный ресурс также является допустимым подходом к управлению текущим состоянием ресурса.Я бы не чувствовал себя обязанным согласованно относиться к атрибутам "состояния" ресурса и его атрибутам "данных".

Попытка сопоставить ресурсы непосредственно с таблицами серьезно ограничит вас.Не забывайте, что когда вы следуете ограничениям REST, вы внезапно оказываетесь очень ограничены в доступных вам глаголах.Вы должны быть в состоянии компенсировать это творческим подходом к ресурсам, которые вы используете.Ограничение себя одним ресурсом, равным одной таблице, серьезно ограничит функциональность вашего конечного приложения.

Мы регулярно видим, рельсы, ASP.NET MVC и остальных пользователей ФОС проводка вопросы здесь на StackOverflow о том, как делать определенные вещи в пределах ограничений отдыха.Проблема часто заключается не в ограничении REST, а в ограничениях фреймворка в его поддержке RESTful приложений.Я думаю, что важно сначала найти надежное решение проблемы, а затем посмотреть, можно ли это сопоставить с выбранной вами структурой.

Что касается создания модели разрешений, которая существует на более мелком уровне, чем сам ресурс.Помните, что одним из ключевых ограничений REST является гипермедиа.Гипермедиа может использоваться не только для поиска связанных объектов, но и для представления допустимых переходов состояний.Если вы возвращаете представление, содержащее встроенные ссылки, условно основанные на разрешениях, то вы можете контролировать, какие действия могут выполняться кем.т. е.Если у пользователя есть разрешения на разблокировку сообщения 342, то вы могли бы вернуть следующую ссылку, встроенную в представление:

<Link href="/UnlockedPosts?url=/Post/342" method="POST"/>

Если у них нет такого разрешения, то не возвращайте ссылку.

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

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

Другие советы

Недавно я обнаружил решение для аутентификации, которое, похоже, решает большинство моих проблем.Если вам понравился этот вопрос, возможно, он вас заинтересует:

https://github.com/stffn/declarative_authorization

Как заметил Даррелл, ОТДЫХ - это не ГРУБОСТЬ.Если вы обнаружите, что идентифицированные вами ресурсы слишком грубые, что унифицированный интерфейс не обеспечивает достаточного контроля, разделите свой ресурс на подресурсы и используйте исходный ресурс как "коллекцию" гиперссылок на его компоненты.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top