REST API - фильтры, дочерние объекты или утечка информации действительно так плоха?

Основная проблема

У меня отдыхает API. Для этого примера предположим, что у меня есть пользователи, они могут быть членами в любом количестве групп, и как пользователи, так и группы могут владеть объектами.

Любой пользователь может фильтровать объекты по различным критериям:

/objects?color=green

/objects?created=yesterday

но только члены данной группы могут фильтровать по принадлежности группы:

/objects?groupId=1

и только фактический пользователь может фильтровать по владению пользователем:

/objects?userId=55

Теперь есть два основных шаблона - можно сделать объект дочерним элементом группы, например:

/groups/4/objects/1

где 4 - это идентификатор группы, а 1 - идентификатор объекта. другой вариант - расположить группу и объекты рядом:

/groups/4

и

/objects/1

превращение объекта в дочерний по отношению к группе и / или пользователю устранит другие параметры фильтрации - по сути, у меня есть один объект с несколькими путями к нему.

Актуальный вопрос

Если я хочу ограничить доступ для обычного пользователя, чтобы он мог получить доступ только к объектам, которые принадлежат непосредственно ему / ей или группам, членом которых он / она является, это действительно работает как фильтр для коллекции, но как насчет уровня сущности?

Если я попробую:

/objects/9

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

Итак, я придумал это:

/objects/9?groupId=4

or

/objects/9?userId=55

В этом случае я могу основывать первоначальное решение об авторизации на идентификаторе группы или пользователя, а затем попытаться получить объект с дополнительным ограничением.

Если пользователь НЕ является членом группы 4, я могу сказать not authorized, а если книга не существует, я могу сказать not found, имея в виду не то, что объект не существует, а то, что объект не существует в группе 4. Этот ответ более ясен, и мне не нужно сначала извлекать объект.

Альтернативой может быть возврат ошибки авторизации независимо от того, вызвана ли она тем, что я не авторизован, ИЛИ из-за того, что объект не существует. Этот ответ немного неточен, но вызовет меньшую нагрузку на вызывающего абонента.

Другая возможность - отобразить несколько путей:

/objects
/groups/4/objects
/users/9/objects
/colors/green/objects

Это кажется довольно запутанным и нарушит принцип наличия единого пути для единой концепции.

Есть ли у кого-нибудь практическое понимание этого? Какие-либо причины (помимо упомянутых), почему тот или иной вариант предпочтительнее?


person rmalchow    schedule 23.04.2015    source источник
comment
Один ресурс МОЖЕТ быть идентифицирован множеством разных URI в соответствии со спецификациями HTTP w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6   -  person Robert    schedule 24.04.2015


Ответы (2)


Если я правильно понимаю, каждый объект связан (по крайней мере) с одной группой или (по крайней мере) с одним пользователем, поэтому у вас нет проблемы с объектом без группы или пользователя.

Если это так, я не вижу смысла в использовании фильтров, поскольку это не имеет смысла в REST-методе, а также не дает никаких преимуществ клиентскому сайту.

Итак, как вы предложили, вы можете просто использовать следующее:

/groups/$groupID/objects/$objectID

и

/user/$userID/objects/$objectID

Теперь ваш сервер должен проверить, авторизован ли клиент («пользователь является членом группы» / «текущий пользователь») с учетом $groupID x или $userID

  • если не авторизован: даже не проверять, есть ли там объект. Просто дайте неавторизованную ошибку.
  • если разрешено: указать стандартные коды ответа

Я не вижу выгоды для неавторизованного клиента в получении информации, если ресурс доступен или нет, поскольку это не важно для него, потому что он не может получить к нему доступ в любом случае. И, как вы предположили, это приведет к утечке информации , что может привести к проблеме безопасности (но это полностью зависит от вашего API, его информации и использования).


Теперь давайте рассмотрим сценарии групповых звонков:


Пользователь Арнольд (участник групп: 1,2,3) хочет получить доступ к существующему объекту 7 группы участников 3.

GET /groups/3/objects/7

ответ: #200


Пользователь Арнольд (участник групп: 1,2,3) хочет получить доступ к несуществующему объекту 55 своей группы участников 2.

GET /groups/2/objects/55

ответ: #404


Пользователь Арнольд (член групп: 1,2,3) хочет получить доступ к существующему объекту 11 из не входящей в группу 5.

GET /groups/5/objects/11

ответ: #401


Пользователь Арнольд (член групп: 1,2,3) хочет получить доступ к несуществующему объекту 19 из группы, не являющейся членом 5.

GET /groups/5/objects/19

ответ: #401




А для пользовательских объектов:


Пользователь Арнольд хочет получить доступ к своему несуществующему объекту 56.

GET /user/arnold/objects/56

ответ: #404


Пользователь Арнольд хочет получить доступ к своему существующему объекту 13.

GET /user/arnold/objects/13

ответ: #200


Пользователь Арнольд хочет получить доступ к существующему объекту Джона 77.

GET /user/jon/objects/77

ответ: #401


Пользователь Арнольд хочет получить доступ к несуществующему объекту Джона 88.

GET /user/jon/objects/88

ответ: #401


Как видите, сервер просто отвечает #401, если клиент не авторизован. Дополнительно было бы неплохо дать сообщение об ошибке в теле, например. Sorry, but you are not authorized to see content of user "Jon" или Sorry, but you are not authorized to see content of group "ABYZX", чтобы клиент знал, в чем проблема.

Это кажется довольно запутанным и нарушит принцип наличия единого пути для единой концепции.

Я так не вижу, как и разные источники (1) (3) и В ответах говорится, что действительно нет проблем с несколькими путями или URI.

Каждый ресурс в наборе услуг будет иметь по крайней мере один URI, идентифицирующий его.

Это может помочь клиентам понять процесс авторизации и тем самым помочь им ориентироваться в вашем API.

person Robert    schedule 23.04.2015
comment
Здравствуйте, Роберт, я согласен с вашим решением при ограниченных условиях ... но я также заметил, что в моем вопросе недооценен элемент наличия нескольких отношений, которые могут считаться родителями - у меня был только административный пользователь, который может получить полный список ... который не совсем так ясно. Я отредактировал свой вопрос. - person rmalchow; 24.04.2015
comment
что будет хотя бы один URL для каждого критерия, относящегося к авторизации? и совместить их невозможно - по крайней мере, это было бы довольно странно. - person rmalchow; 26.04.2015

Подойдет любой из описанных вами вариантов URI. Я предпочитаю плоские URI, но это не имеет особого значения.

Настоящий вопрос в том, как обрабатывать неавторизованные запросы. В этом случае я предлагаю отвечать 404 во всех случаях, в том числе когда ресурс существует, но у пользователя нет доступа. Это позволяет избежать проблемы утечки информации и полностью совместимо со спецификацией HTTP.

Этот паттерн имеет логический смысл. С точки зрения пользователя с недостаточными правами, ресурс не существует.

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

person Jason Desrosiers    schedule 24.04.2015