Как стать автором
Обновить

Комментарии 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. Проверку вы делаете в ресолверах, но сама логика делегируется куда-то ниже по дереву зависимостей.


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

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

НЛО прилетело и опубликовало эту надпись здесь
Также, как если бы в ответе вернулось null.
Если тип поля NonNull, то выдает ошибку: Cannot return null for non-nullable field
Если нет, то просто возвращает значение как null.
НЛО прилетело и опубликовало эту надпись здесь
Совершенно верно. )
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации