Pull to refresh

Comments 16

Хотелось бы все же примеры различных стратегий борьбы с N+1 проблемами.

И хоть в комментариях к предыдущей статье уже не раз говорилось о том, что безопасность вашего приложения находится в ваших руках, а не руках GraphQL, я все же покажу пару простых способов обезопасить свое приложение при использовании graphql-php.


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

Конечно мы им, обычно, даём разные урлы (в вашем случае типа localhost/graphql.php )
Это мы на уровни роли разрулим у себя на сервере нашего приложения.

Но, обычно мы знаем (через специфические URI запроса) — что посылать обратно клиенту.
В случае GraphQL — мы имеем большую возможность, что юзер с ролью пришлёт такой запрос на который (на часть которого) у него нет разрешения (нет политики).

К примеру, юзер с ролью анонимус, запрашивая имя, к примеру, не имеет права видеть номер телефон в своём ответе, хотя в своём GraphQL-запросе он это потребовал (указал)!

Вот как разграничить это удобно? И желательно на стадии когда запрос, пришедший на GraphQL сервер для обработки, ещё не выполнился.

Напрашивается, что в этом случае было бы здорово как-то «навешать роли» на схему GraphQL?

Иначе при обработки запроса потребуется через типа if проверять — а имеет ли право эта роль требовать чтобы ей в ответе прислали номер телефона?

Вот о какой безопасно идёт речь, в случае GraphQL. Имхо.

Вот о какой безопасно идёт речь, в случае GraphQL. Имхо.

Просто «в комментариях к предыдущей статье» в основном спрашивали про тяжелые запросы и nested attacks.

Что касается вашего вопроса:
Вот как разграничить это удобно?

Я пожалуй отвечу так: А как вы это делаете сейчас (без GraphQL)?
Конечно мы им, обычно, даём разные урлы

Ну вообще-то урлам (а точнее ресурсам) дела нет до ваших ролей. Так что плохая идея.


Вот как разграничить это удобно?

пользователь запросил несколько филдов. К некоторым из них у него нет доступа. Стало быть для этих филдов мы ничего не возвращаем и добавляем информацию об ошибках.


было бы здорово как-то «навешать роли» на схему GraphQL?

это будет работать только в самых примитивных случаях. Как правило все намного сложнее. В документации к graphql есть вполне себе неплохой момент описывающий основную суть проблемы: Делегируй авторизацию действий слою с бизнес логикой. То есть в ресолвере мы лишь запрашиваем данные, а чуть что — ловим исключение и добавляем в список ошибок для полей.


Иначе при обработки запроса потребуется через типа if проверять — а имеет ли право эта роль требовать чтобы ей в ответе прислали номер телефона?

Давайте так, вот вам бизнес правило: email пользователя можно показывать только самому пользователю, администратору, модератору, друзьям пользователя. Тут уже просто ролями не отделаешься и вам придется делать if. А так как это "бизнес" правило — его надо проверять на уровне бизнес логики а не во "вьюхе" (а именно вьюхой является резолверы в graphql).


Вот о какой безопасно идёт речь, в случае GraphQL. Имхо.

Graphql = view layer. Authorization = business layer. Я думаю идея понятна.

XAHTEP26
Я пожалуй отвечу так: А как вы это делаете сейчас (без GraphQL)?
Очень по разному.

Начиная от доступа к URI по ролям, использование ролей при выводе результатов (уже после основного запроса/запросов) и заканчивая — ставим роль на конкретный метод сервиса (чтоб уж наверняка не «пробило»).

Но дело в том, что мы сейчас знаем — что выдать клиенту — ибо фактически не он, а мы управляем запросом.
А вот GraphQL — тут первую скрипку ведёт клиент. И в этом то и отличие.


Fesor
пользователь запросил несколько филдов. К некоторым из них у него нет доступа. Стало быть для этих филдов мы ничего не возвращаем и добавляем информацию об ошибках.
Не факт.
Нам надо как-то понять, что к этой инфе у него нет доступа (например на основании роли — но это надо
где-то прописать).
Вернуть ему можно и null и без ошибки. Или ничего не вернуть и ошибку — но это нюансы реализации, я думаю.

Fesor
это будет работать только в самых примитивных случаях. Как правило все намного сложнее. В документации к graphql есть вполне себе неплохой момент описывающий основную суть проблемы: Делегируй авторизацию действий слою с бизнес логикой. То есть в ресолвере мы лишь запрашиваем данные, а чуть что — ловим исключение и добавляем в список ошибок для полей.
Да, автор пишет — «It is tempting to place authorization logic in the GraphQL layer like so:» И действительно заманчиво туда и помещать — в схему GraphQL.

Автор просто «выбрасывает» проверку роли во вне. Но почему? — Тогда нам придётся во вне строить дерево доступа по ролям.

Не просто «заманчиво» — а нужно помещать роли в в схему GraphQL.

Фактически обработка запроса от клиента должна быть типа такой:

  • дерево-запроса-0 — исходное дерево запроса от клиента.
  • 1) функция от дерева-запроса-0 -> проверка дерева-запроса-0 на корректность и выдача ошибки (или null в ветке). На выходе дерево-запроса-1
  • 2) функция от дерева-запроса-1 -> проверка дерева-запроса-1 на допустимость выдачи (в зависимости от роли клиента) и выдача ошибки (или null в ветке где недопустимо). На выходе дерево-запроса-2
  • 3) функция от дерева-запроса-2 -> выполнение запроса и выдача ошибки (если что-то пошло не так с базой...). На выходе дерево-ответа.
  • Отправка клиенту дерево-ответа.


Fesor
Давайте так, вот вам бизнес правило: email пользователя можно показывать только самому пользователю, администратору, модератору, друзьям пользователя. Тут уже просто ролями не отделаешься и вам придется делать if.


Верно. Идеально чтобы там не просто задать список ролей, а был примитивный язык — как он сейчас во многих фреймворках и есть.

Понимаете, на практике, при отсутствие просто настроить доступ по ролям, будет либо распределение URI по ролям (что грубо), либо, если это публичный сервис — типа Google-map — который ничего не скрывает и роли ему до фонаря.

Имхо, конечно, имхо.

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

Начиная от доступа к URI по ролям, использование ролей при выводе результатов (уже после основного запроса/запросов) и заканчивая — ставим роль на конкретный метод сервиса (чтоб уж наверняка не «пробило»).

Вот точно так же и с GraphQL. )

Но дело в том, что мы сейчас знаем — что выдать клиенту — ибо фактически не он, а мы управляем запросом.
А вот GraphQL — тут первую скрипку ведёт клиент. И в этом то и отличие.

Клиент может выбирать какие данные запросить из тех, которые ему доступны и задавать их структуру. Вам же остается только ограничить те данные, которые доступны клиенту (как вы это и делаете сейчас) и пусть балуется.
А вот GraphQL — тут первую скрипку ведёт клиент. И в этом то и отличие.

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


Автор просто «выбрасывает» проверку роли во вне. Но почему? — Тогда нам придётся во вне строить дерево доступа по ролям.

Потому что проверка прав не является обязанностью view layer.


Не просто «заманчиво» — а нужно помещать роли в в схему GraphQL.

это как помещать бизнес правила в шаблоны. Вы же так не делаете?


Фактически обработка запроса от клиента должна быть типа такой:

как-то так и происходит, просто все эти решения делегируются вашему коду.


Верно. Идеально чтобы там не просто задать список ролей, а был примитивный язык — как он сейчас во многих фреймворках и есть.

Зачем вам примитивный язык если можно написать вполне себе нормальным кодом?


Понимаете, на практике, при отсутствие просто настроить доступ по ролям, будет либо

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


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

вам никто не мешает сделать простенький декоратор/ивент листенер для ресорверов каждой ноды дерева. Собственно именно так это делается "во многих фреймворках". Просто это не входит в обязанности graphql, но вас никто не останавливает.

XAHTEP26
Клиент может выбирать какие данные запросить из тех, которые ему доступны и задавать их структуру. Вам же остается только ограничить те данные, которые доступны клиенту (как вы это и делаете сейчас) и пусть балуется.
Сейчас мне проще это сделать ибо у меня множество URI (точек входа — «дверей») которым я могу сопоставить роли.

В случае GraphQL у меня будет фактически одно URI — "одно окно" — и возникает вопрос — где мне прописывать ограничения?

Fesor
Зачем вам примитивный язык если можно написать вполне себе нормальным кодом?
Примитивные языки появляются не потому что нельзя писать «нормальным кодом» — а потому что писать в этом случае «нормальным кодом» неудобно из-за boilerplate code.

Fesor
вам никто не мешает сделать простенький декоратор/ивент листенер для ресорверов каждой ноды дерева. Собственно именно так это делается «во многих фреймворках». Просто это не входит в обязанности graphql, но вас никто не останавливает.

Из
этой картинки
image

Хорошо видно куда они задвинули авторизацию. Но если принято решание использовать только и только graphql — то возникает смысл авторизацию двинуть в описание схемы.

Иначе придётся «городить» что-то вроде строк (где-то рядом в файле настройке доступа):
user / name: all
user / {name, telephon}: admin

Фактически превращая ветки и листья дерева ответа в линейные строки — и это только для того чтобы настроить доступ то!

Вся эта настройка просто просится разместиться в GraphQL-схеме — ей там самое место.

Иначе будет не айс.
Примитивные языки появляются не потому что нельзя писать «нормальным кодом» — а потому что писать в этом случае «нормальным кодом» неудобно из-за boilerplate code.

давайте на конкретных примерах. Приведите мне удобный для вас DSL описания прав а дальше будем рассматривать "чем он плох".


Хорошо видно куда они задвинули авторизацию.

я вам больше скажу, ее и в других вариантах (http api, rpc) задвинули тудаже и правильно сделали. однако вас никто не останавливает ее оттуда вытащить и сделать что-то типа мидлвари.


Но если принято решание использовать только и только graphql — то возникает смысл авторизацию двинуть в описание схемы.

role-based авторизация удобна только для простых случаев. Даже в случае с админкой у меня есть проекты где все вроде и админы но права у всех разные. Ну либо мне надо генерить еще и пермутации маленьких "ролей".


Вся эта настройка просто просится разместиться в GraphQL-схеме — ей там самое место.

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

Сейчас мне проще это сделать ибо у меня множество URI (точек входа — «дверей») которым я могу сопоставить роли.

В случае GraphQL у меня будет фактически одно URI — «одно окно» — и возникает вопрос — где мне прописывать ограничения?

Да ведь можно же ограничить сам список полей в схеме. Например, если роль «Авторизованный пользователь», то он может запрашивать поле «messages», а если «Гость», то нет (возвращать null или выводить ошибку).

И в конце концов, если вам хочется, можно сделать несколько endpoint`ов (URI/точек входа/дверей/окон) с разными схемами данных и сопоставлять им соответствующие роли.
Fesor
давайте на конкретных примерах. Приведите мне удобный для вас DSL описания прав а дальше будем рассматривать «чем он плох».
Не будем. Язык называется SpEL и он на своём месте.

XAHTEP26
И в конце концов, если вам хочется, можно сделать несколько endpoint`ов (URI/точек входа/дверей/окон) с разными схемами данных и сопоставлять им соответствующие роли.
Нет. Понимаете GraphQL — это всё равно «одно окно». Сама логика GraphQL стремится к «одно окно».

Сейчас у меня всё просто. Есть список URI — он двухмерный:
Сверху вниз:

<sec:intercept-url access="IS_AUTHENTICATED_ANONYMOUSLY" pattern="/home.html"/>
<sec:intercept-url access="IS_AUTHENTICATED_ANONYMOUSLY" pattern="/search.html"/>
...
<sec:intercept-url access="ROLE_PERSON_VIEWER" pattern="/person-edit.html"/>
<sec:intercept-url access="ROLE_PERSON_VIEWER,ROLE_PERSON_EDITOR"/> pattern="/delete-photos.json"
...


То есть сверху строки — всем разрешено, а потом идёт снижение доступа до определённых ролей. — Чем ниже строка тем больший приоритет она имеет.

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

В случае же GraphQL, который логикой своей влечёт к «одному окну» — такой «фокус не проходит. И если как-то его реализовать — то будет чужеродным.

В случае GraphQL роли надо вставлять в саму схему GraphQL. Иначе будет чужеродно.

То есть на данном этапе GraphQL можно использовать только и только как public выдача — где скрывать или ограничивать нечего — что клиент запросил то и получил. Типа Google map и т.п.

В ином случае использовать GraphQL будет проблематично и даже очень проблематично.

Имхо.
Не будем. Язык называется SpEL и он на своём месте.

Неплохой DSL. Реально удобный, по сравнению с java. Но тут есть нюансы:


  • его надо изучать. В этом собственно вся проблема spring integration. Невероятно мощная штука которая заставляет тебя не код писать а тонны конфигов.
  • если мы говорим не про Java а скажем про JavaScript/TypeScript, все это можно сделать лаконичнее. Да еще и с композицией. Потому все же когда мы говорим про "удобно" стоит все же думать о контексте.

Если нужно, я использую проверку в контроллере или на выходе (на JSP — там в виде тега, но это неважно)

Но сами бизнес правила вы же делегируете неким другим объектам? Не в контроллерах же у вас бизнес правила? Точно так же и в graphql. Проверку вы делаете в ресолверах, но сама логика делегируется куда-то ниже по дереву зависимостей.


такой «фокус не проходит. И если как-то его реализовать — то будет чужеродным.

Почему? Почему не вынести проверки на уровень декораторов над ресолверами? Это жа намного удобнее.

UFO just landed and posted this here
Также, как если бы в ответе вернулось null.
Если тип поля NonNull, то выдает ошибку: Cannot return null for non-nullable field
Если нет, то просто возвращает значение как null.
UFO just landed and posted this here
Only those users with full accounts are able to leave comments. Log in, please.