Re[9]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: dimgel Россия https://github.com/dimgel
Дата: 08.02.14 04:36
Оценка:
Здравствуйте, fddima, Вы писали:

F> Я в большей степени по этому и удивлён каким-то этим спорам, когда сравнивается одна фундаментальная вещь (RPC) с модно-конкретной (REST).


Можно считать, что REST — это паттерн. Внутри приложения тоже можно просто как попало non-remote методы вызывать, безо всяких паттернов.

F> PS: POST — есть Create. PUT — есть Update. Реализация идемпотентности к POST (а она нужна) — зависит полностью от тебя, что бы там умы не говорили.


В моей исходной цитате PUT предлагалось в качестве add to store. Хочешь сказать, что это не create?
Re[10]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: fddima  
Дата: 08.02.14 04:43
Оценка: 8 (1) -1
Здравствуйте, dimgel, Вы писали:

F>> PS: POST — есть Create. PUT — есть Update. Реализация идемпотентности к POST (а она нужна) — зависит полностью от тебя, что бы там умы не говорили.

D>В моей исходной цитате PUT предлагалось в качестве add to store. Хочешь сказать, что это не create?
Думаю, что create — всё таки должен быть POST. Хотя PUT, в случае когда id (url) создаваемого ресурса заранее известен — наверное тоже может быть применён. Но я бы так не делал. Да и мне не встречались чужие сервисы которые бы так делали.
CRUD = PGPD (POST — GET — PUT — DELETE)
Re[11]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.02.14 06:46
Оценка:
Здравствуйте, Cyberax, Вы писали:
C>Ну вот нынче писали REST API. Ну и @!@$*&@# этот REST. Скажем, несколько объектов в одной транзакции поменять нормально нельзя.
Это да, транзакции требуют специального проектирования — вместо 2х PUT на объект account делается 1 PUT на объект transfer.
C>В теории есть метод PATCH, который делает частичные обновления, но знаю про него только я.
А он вам часто нужен? Почему нельзя заэкспозить нужные части как отдельные адресуемые объекты?
C>Любой надёжный метод делается через полную задницу, в два шага (получить тикет, потом сделать запрос).
Почему ваш любой надёжный метод не свёлся к PUT? Зачем вам получать тикет — на клиенте нет генератора GUID?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 08.02.14 07:25
Оценка:
Здравствуйте, Cyberax, Вы писали:

S>>То есть вместо возврата непрозрачных идентификаторов, которые нужно как-то угадать куда вставлять, в ресте рекомендуется возвращать прямо честные URL, по которым можно сделать GET (ну, или другие глаголы).

C>Ну вот нынче писали REST API. Ну и @!@$*&@# этот REST. Скажем, несколько объектов в одной транзакции поменять нормально нельзя.
C>В теории есть метод PATCH, который делает частичные обновления, но знаю про него только я.
C>Любой надёжный метод делается через полную задницу, в два шага (получить тикет, потом сделать запрос).

Ну так он и по любому RPC делается точно так же. Двухфазную фиксацию в СУБД придумали не зря. Построение транзакций со стороны клиента так, чтобы проверять в случае обрыва, прошла она или нет — тоже.

Нельзя обмануть фундаментальные физические законы, а несинхронность состояния клиента и сервера в процессе их обмена можно приравнять к таким законам, или напрямую вычислить из них. Можно только сделать средства контроля этого настолько занудным, насколько хватит.
The God is real, unless declared integer.
Re[11]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 08.02.14 10:56
Оценка: +1
Здравствуйте, fddima, Вы писали:

F> Думаю, что create — всё таки должен быть POST. Хотя PUT, в случае когда id (url) создаваемого ресурса заранее известен — наверное тоже может быть применён. Но я бы так не делал. Да и мне не встречались чужие сервисы которые бы так делали.

F> CRUD = PGPD (POST — GET — PUT — DELETE)

Мы делаем. Нет никакой проблемы разрешить клиенту генерировать uuid, когда есть разделение прав и явное указание создать.
The God is real, unless declared integer.
Re[11]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.02.14 16:35
Оценка:
Здравствуйте, fddima, Вы писали:
F> Думаю, что create — всё таки должен быть POST.
Только если нет другого выхода, или когда не страшно наплодить дубликатов. Например, через POST удобнее всего получать тикеты для последующих идемпотентных операций — неиспользованные тикеты ничего не стоят.
F>Хотя PUT, в случае когда id (url) создаваемого ресурса заранее известен — наверное тоже может быть применён.
Именно.
F>Но я бы так не делал.
А зря. Зачем мучиться, приделывая идемпотентность к POST, когда есть готовый понятный PUT?
F>Да и мне не встречались чужие сервисы которые бы так делали.
Полно таких сервисов. Вот первое, что попалось: http://msdn.microsoft.com/en-us/library/windowsazure/dd179451.aspx
F> CRUD = PGPD (POST — GET — PUT — DELETE)
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: dimgel Россия https://github.com/dimgel
Дата: 09.02.14 16:39
Оценка:
Здравствуйте, Sinclair, Вы писали:

F>>Хотя PUT, в случае когда id (url) создаваемого ресурса заранее известен — наверное тоже может быть применён.

S>Именно.

Эм... А часто ли он известен? Тут как бы по ощущениям подразумевается, что в базе PK — GUID-ы, генерируемые как раз как номера тикетов, так?
Re[13]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: dimgel Россия https://github.com/dimgel
Дата: 09.02.14 16:42
Оценка:
D>Здравствуйте, Sinclair, Вы писали:

F>>>Хотя PUT, в случае когда id (url) создаваемого ресурса заранее известен — наверное тоже может быть применён.

S>>Именно.

D>Эм... А часто ли он известен? Тут как бы по ощущениям подразумевается, что в базе PK — GUID-ы, генерируемые как раз как номера тикетов, так?


Хотя GUID-ы клиент и сам генерить может. А если подсунет левый и что-нибудь из доступных ему на запись данных сломает, тут уж звиняйте, ССЗБ.
Re[6]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.02.14 16:43
Оценка: 8 (1)
Здравствуйте, dimgel, Вы писали:
D>А почему добавление в store — PUT, а в collection — POST? Причём изменение ресурса, в т.ч. элемента коллекции — тоже PUT, то нафига тут POST вообще? Если дело в том, что PUT идемпотентный, то может у меня store допускает задвоения экземпляров?
Это трудности перевода. PUT — это не "добавление" в store, это просто запись в хранилище. В то, которое идентифицировано URL, переданным в PUT.
Например,

PUT http://dormitoryservice.nsu.ru/dorm6/314/1 
...

{
  Name='Vasily'
}

— на первой койке в комнате 314 теперь живёт Василий.
POST, если речь о добавлении чего-то — это "вставь куда-нибудь сюда":
PUT http://dormitoryservice.nsu.ru/dorm6/
...

{
  Name='Vasily'
}

Василий будет прописан куда-то на свободное место. Надо понимать, что отсутствие идемпотентности означает, что повторный вызов приведёт к тому, что на Василия будет записано две койки (если не принимать специальных мер). Это радикально отличается от семантики PUT, и даёт нам ключ к пониманию применимости: если мы знаем адрес нового объекта, то нам достаточно PUT. Если адрес должен придумать сервис, то придётся перейти на POST. При этом сразу надо думать о том, как избежать дубликатов в случае сбоев. Обычно это достигается соглашениями типа Primary Key — т.е. добавляем в данные номер паспорта, и в случае повторов отправляем код 4xx, предоставляя клиенту разбираться с тем, почему так получилось. Это несколько усложняет клиента — в случае идемпотентной операции признак успеха — код 2хх, а 4хх означает ошибку. Теперь нам надо внимательно смотреть на код ошибки и разбираться, то ли мы чего-то накосячили и Василий остался без койки, то ли всё уже хорошо, просто в прошлый раз мы этого не поняли.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.02.14 16:57
Оценка: 6 (1)
Здравствуйте, dimgel, Вы писали:

D>Эм... А часто ли он известен? Тут как бы по ощущениям подразумевается, что в базе PK — GUID-ы, генерируемые как раз как номера тикетов, так?

Он известен ровно тогда, когда это предусмотрел разработчик протокола
То есть — одно из трёх:
— естественные ключи (рейс-дата-номер места)
— распределённо-генерируемые искусственные ключи (читай — GUID)
— централизованно-генерируемые искусственные ключи (читай — тикеты).

Случаев, когда адрес нового объекта ну совсем-совсем неизвестен, нужно избегать, т.к. они приносят слишком много геморроя.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.02.14 17:06
Оценка: 4 (1)
Здравствуйте, dimgel, Вы писали:
D>Хотя GUID-ы клиент и сам генерить может. А если подсунет левый и что-нибудь из доступных ему на запись данных сломает, тут уж звиняйте, ССЗБ.
Да. Основное преимущество GUID-ов — в том, что их можно генерировать полностью локально. "Подсовывание левого" является неотъемлемым правом клиента. Если он решил всю свою коллекцию порнухи сохранить под одним и тем же гуидом, то и флаг ему в руки — как мы отличим его от легитимного "ой, нет, я лучше сюда другой фильм закачаю"?
А с правами доступа разобраться достаточно легко. Если для конкретного клиента можно выделить песочницу — так и делаем, конфликты игнорируем. Т.е. я пишу в .../sinclair/001/ одно, вы — в .../dimgel/001/ другое, и мы друг другу никак не мешаем. Если я могу дать права dimgel на запись в /sinclair, то уже нужно тщательнее следить за руками. GUID хорош тем, что случайно получить такой же, как у соседа, невозможно. То есть вы должны сначала как-то подсмотреть один из существующих GUIDов в моей песочнице, и уже потом чего-то туда писать.

Если используются не GUID, а что-то с большей вероятностью конфликтов, или если нам важно избежать случайной перезаписи чужих изменений, то в REST есть специальный паттерн "Версионирование". Т.е. читать я могу из /sinclair/001/ (получу 301 на /sinclair/001/0011, где 0011 — номер последней версии), а вот записывать надо в /sinclair/001/0012/, где 0012 — это номер новой версии. Если такая версия уже есть, то я получаю 409 Conflict — это означает, что кто-то успел вклиниться между моими чтением и записью. Обычно это повод перечитать объект, и начать принятие решения с нуля — может быть, версия 0012 от конкурента лучше, чем то, что я хотел в неё записать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Cyberax Марс  
Дата: 09.02.14 20:43
Оценка:
Здравствуйте, Sinclair, Вы писали:

C>>Ну вот нынче писали REST API. Ну и @!@$*&@# этот REST. Скажем, несколько объектов в одной транзакции поменять нормально нельзя.

S>Это да, транзакции требуют специального проектирования — вместо 2х PUT на объект account делается 1 PUT на объект transfer.
Ага, особенно если нужно штучек так 100 объектов поменять. Кстати, для множественных операций в REST тоже ничего особого нет.

C>>В теории есть метод PATCH, который делает частичные обновления, но знаю про него только я.

S>А он вам часто нужен? Почему нельзя заэкспозить нужные части как отдельные адресуемые объекты?
Ну вот оказался нужен. Но почти никакие клиентские библиотеки его не поддерживают.

C>>Любой надёжный метод делается через полную задницу, в два шага (получить тикет, потом сделать запрос).

S>Почему ваш любой надёжный метод не свёлся к PUT? Зачем вам получать тикет — на клиенте нет генератора GUID?
Нельзя доверять тому, что у клиента UUID'ы будут надёжными. Так что надо или делать stateful-сессии и беспокоиться об уникальности UUID'ов только внутри них, или серверные тикеты. В принципе, получается то же самое — один лишний database write на запрос.

В общем, надёжность должна быть на уровне протокола.
Sapienti sat!
Re[13]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.14 05:21
Оценка:
Здравствуйте, Cyberax, Вы писали:
C>Ага, особенно если нужно штучек так 100 объектов поменять. Кстати, для множественных операций в REST тоже ничего особого нет.
Да хоть 1000 — принцип тот же.

C>Ну вот оказался нужен.

А подробнее можно?
C>Нельзя доверять тому, что у клиента UUID'ы будут надёжными.
Почему?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Cyberax Марс  
Дата: 10.02.14 05:32
Оценка:
Здравствуйте, Sinclair, Вы писали:

C>>Ага, особенно если нужно штучек так 100 объектов поменять. Кстати, для множественных операций в REST тоже ничего особого нет.

S>Да хоть 1000 — принцип тот же.
Ну да, то есть никакого.

C>>Ну вот оказался нужен.

S>А подробнее можно?
Оказалось нужно различить PUT с дефолтными значениями и PATCH изменившихся.

C>>Нельзя доверять тому, что у клиента UUID'ы будут надёжными.

S>Почему?
Делаем такую последовательность:
user_id = create_new_user()
assign_roles_to_user(user_id, admin_role_id)


Если враг может сделать так, что для create_new_user() будет конфликт UUID'ов, то есть шанс подменить user_id на контролируемый врагом.
Sapienti sat!
Re[15]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.14 06:15
Оценка:
Здравствуйте, Cyberax, Вы писали:
S>>Да хоть 1000 — принцип тот же.
C>Ну да, то есть никакого.
Что значит "никакого"? Принцип — делаем PUT в объект "Transaction".

S>>А подробнее можно?

C>Оказалось нужно различить PUT с дефолтными значениями и PATCH изменившихся.
Не до конца понимаю. Это же ваш протокол, что мешало внести в него понятия (reset-to-default), (clear), (leave-as-is) в дополнение к установке конкретного значения?

S>>Почему?

C>Делаем такую последовательность:
C>
C>user_id = create_new_user()
C>assign_roles_to_user(user_id, admin_role_id)
C>


C>Если враг может сделать так, что для create_new_user() будет конфликт UUID'ов, то есть шанс подменить user_id на контролируемый врагом.

Продолжаю не понимать. То есть вы хотите сказать, что клиент 1 (привилегированный) делает типа assign_roles(admin_role), а клиент 2 (непривилегированный) делает reset_password(), и всё это на один и тот же user_id?
Пока что я не могу себе представить, каким образом клиент 2 может предсказать GUID, сгенерированный на клиенте 1.

Меня пугает непонятное — расскажите поподробнее. (Вопрос не праздный, если сценарий, о котором вы говорите, реализуем, то существует уязвимость, актуальная для всех нынешних и будущих приложений на стандарте APS2)
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Jack128  
Дата: 10.02.14 06:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>Почему?

C>>Делаем такую последовательность:
C>>
C>>user_id = create_new_user()
C>>assign_roles_to_user(user_id, admin_role_id)
C>>


C>>Если враг может сделать так, что для create_new_user() будет конфликт UUID'ов, то есть шанс подменить user_id на контролируемый врагом.

S>Продолжаю не понимать. То есть вы хотите сказать, что клиент 1 (привилегированный) делает типа assign_roles(admin_role), а клиент 2 (непривилегированный) делает reset_password(), и всё это на один и тот же user_id?
S>Пока что я не могу себе представить, каким образом клиент 2 может предсказать GUID, сгенерированный на клиенте 1.
А зачем предсказывать?? ставишь снифер клиенту1 и видишь, что тот обращается по урлу www.example.com/api/users/F889A953B7C543AB82762A2E02E299BF вот те гуид и есть. Другое дело, что если "клиент 2 (непривилегированный)" может обресетить пароль, то это вообще какая та странная безопасность. И никакими тикитами с сервера тут не защитится.
Re[16]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Cyberax Марс  
Дата: 10.02.14 06:32
Оценка: 4 (1)
Здравствуйте, Sinclair, Вы писали:

S>>>Да хоть 1000 — принцип тот же.

C>>Ну да, то есть никакого.
S>Что значит "никакого"? Принцип — делаем PUT в объект "Transaction".
Сотню запросов с латентностью в 200-300 миллисекунд? Ну-ну.

C>>Оказалось нужно различить PUT с дефолтными значениями и PATCH изменившихся.

S>Не до конца понимаю. Это же ваш протокол, что мешало внести в него понятия (reset-to-default), (clear), (leave-as-is) в дополнение к установке конкретного значения?
Код библиотеки на JS так не умеет.

C>>Если враг может сделать так, что для create_new_user() будет конфликт UUID'ов, то есть шанс подменить user_id на контролируемый врагом.

S>Продолжаю не понимать. То есть вы хотите сказать, что клиент 1 (привилегированный) делает типа assign_roles(admin_role), а клиент 2 (непривилегированный) делает reset_password(), и всё это на один и тот же user_id?
Нет. Враг угадывает какой UUID будет у запроса create_new_user() от привиллегированного пользователя и использует этот UUID при создании пользователя под своим контролем.

Затем привиллегированный пользователь делает create_new_user() с этим же UUID'ом и ему попадает уже существующий пользователь, которого создал враг, так как для сервера это кажется просто повторением запроса из-за ошибки клиента.

Ну и дальнейшая эксплуатация может быть разной.

Решения:
1) Сделать клиентский токен полностью алгоритмическим, включив туда ID пользователя и секретные данные. Так поступает, например, Amazon. Недостаток: сложность реализации, не всякий клиент может иметь доступ ко всем нужным HTTP-заголовкам.
2) Серверные тикеты с проверкой авторизации, включая возможность выдать сразу N тикетов.
Sapienti sat!
Re[17]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: dimgel Россия https://github.com/dimgel
Дата: 10.02.14 06:35
Оценка:
Здравствуйте, Jack128, Вы писали:

J>обращается по урлу www.example.com/api/users/F889A953B7C543AB82762A2E02E299BF


А вот меня кстати ещё с книжки смущает кодировать id|login|api_key текущего юзера в URL. Если мне, к примеру, не нужно между юзерами ничего шарить, т.е. они полностью изолированы друго от друга, я хочу юзать /my/contacts вместо /users/KEY/contacts (или вместо прилагательного my принято что-то другое юзать) + HTTP authorization (пишут ещё про какой-то OAuth, но мне в него лениво и наверняка он браузерами не поддерживается).
Re[12]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.02.14 06:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:

S>> Поиск не работает.
S>Зачем поиск? В этой теме не так много постов.

S>> Мне интересно как на физическом уровне. Понятно, что должно где то храниться ID запроса. И насколько это накладно для всех видов запросов.

S>Нет никакого ID запроса. У каждого объекта есть URL. Операции с объектом делятся на safe, idempotent, и операцию POST. Поэтому ничего "отдельно" хранить не надо.
Ну вот я отправляю письмо. Должен ему присвоить какой URL в котом содержится ID этого письма, что бы при повторной посылке POST оно не отправлялось, а получался ответ который до меня не дошел. Так же и с GET если я запрашиваю справочник по ID то согласно idempotent мне по этому ID должен возвращаться всегда одни и теже данные. Идемпотентность
Согласно этому определению

Идемпотентная операция в информатике — действие, многократное повторение которого эквивалентно однократному.

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



Кстати часто возникают проблемы с кэшированием, когда данные изменились, а ответ возвращается один и тот же. Тогда я понимаю что бы избавиться от идемпотентности можно добавить GUID или метку времени в строку запроса?
и солнце б утром не вставало, когда бы не было меня
Re[17]: [давненько мы за REST не срались] Paradigm mismatch is unavoidable!
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.14 06:37
Оценка:
Здравствуйте, Jack128, Вы писали:

J>А зачем предсказывать?? ставишь снифер клиенту1 и видишь, что тот обращается по урлу www.example.com/api/users/F889A953B7C543AB82762A2E02E299BF вот те гуид и есть. Другое дело, что если "клиент 2 (непривилегированный)" может обресетить пароль, то это вообще какая та странная безопасность. И никакими тикитами с сервера тут не защитится.

Вот именно. Случай, когда клиент 2 может сбросить пароль кому угодно — это такая дыра в безопасности, что её и обсуждать не стоит.
Обсуждаемый случай — это угон GUID. Пытаться его угнать после того, как его сгенерировал клиент 1, бесполезно — разделение прав доступа не даст нам ничего сделать. Единственная возможная дыра — это предсказать GUID и "застолбить" его, пользуясь неразличимостью действий "создать" и "изменить" в REST. Поскольку клиент 1 — привилегированный, система безопасности типа даст ему назначить админские права "чужому" аккаунту.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.