RESTful Авторизация - PullRequest
       26

RESTful Авторизация

25 голосов
/ 11 сентября 2009

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

Вопросы:

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

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

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

Не является ли разложение еще одним словом для гнили?

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

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

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

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

Одно замечание, которое я видел, что смягчает некоторые недостатки вышеупомянутого подхода, - это определение разрешений как создание / удаление для ресурсов и чтение / запись для атрибутов . Эта система представляет собой компромисс между атрибутами-ресурсами-ресурсами и привилегиями-ресурсами: в одной из них по-прежнему остается только 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". (Было бы grand , если бы все такие предикаты могли быть выражены в терминах одного пользователя и одного ресурса.) Концептуально вытекающие "окончательные" правила были бы не предикативными. Это поднимает вопрос о том, как реализовать такую ​​систему. Предикаты должны быть членами классов Model, но я не уверен, как можно изящно ссылаться на них в контексте правил. Чтобы сделать это безопасно, потребуется какое-то отражение. Здесь я снова чувствую, что для этого потребуется модификация реализации модели.

Как вы пишете это снова?

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


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

Ответы [ 3 ]

8 голосов
/ 14 сентября 2009

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

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

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

POST /LockedPosts?url=/Post/2010

Подресурс также является допустимым подходом для управления текущим состоянием ресурса. Я бы не чувствовал себя обязанным согласованно относиться к атрибутам «состояния» ресурса и его атрибутам «данных».

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

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

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

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

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

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

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

2 голосов
/ 01 апреля 2011

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

https://github.com/stffn/declarative_authorization

0 голосов
/ 15 сентября 2009

Как отметил Даррел - ОТДЫХ не CRUD. Если вы обнаружите, что ваши идентифицированные ресурсы слишком грубые, что унифицированный интерфейс не обеспечивает достаточного контроля, то разделите ваш ресурс на подресурсы и используйте исходный ресурс как «коллекцию» гиперссылок на его компоненты.

...