Лучшая практика для обработки грантов на уровне столбцов в PostGraphile - PullRequest
1 голос
/ 25 февраля 2020

PostGraphile НЕ рекомендует гранты SELECT на уровне столбца , вместо этого рекомендует

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

Теперь я хочу, чтобы в моей таблице users было поле role, к которому может обращаться role_admin, но не role_consumer. На основании приведенной выше рекомендации я создал две таблицы. Таблица users (в схеме publi c) содержит все поля, которые могут видеть обе роли, а user_accounts (в частной схеме) содержит поле role, которое должно видеть только role_admin. Поле role добавляется к типу user GraphQL через вычисляемые столбцы.

CREATE SCHEMA demo_public;
CREATE SCHEMA demo_private;

/* users table*/
CREATE TABLE demo_public.users (
  user_id SERIAL PRIMARY KEY,
  first_name VARCHAR(50) NOT NULL,
);

/* user_accounts */
CREATE TABLE demo_private.user_accounts (
  user_id INT PRIMARY KEY REFERENCES demo_public.users (user_id) ON DELETE CASCADE,
  role text not null default 'role_consumer',
);

/* role as computed column */
CREATE FUNCTION demo_public.users_role
(
  u demo_public.users
)
RETURNS TEXT as $$
  <code>
$$ LANGUAGE SQL STRICT STABLE;

Теперь в основном у меня есть два зелья для установки разрешений.

1) Первый вариант - использовать безопасность на уровне таблицы. IOW, чтобы предоставить выборочный доступ к таблице user_accounts ТОЛЬКО role_admin.

GRANT SELECT ON TABLE demo_private.user_accounts TO role_admin;
GRANT EXECUTE ON FUNCTION demo_public.users_role(demo_public.users) TO role_admin;
ALTER TABLE demo_private.user_accounts ENABLE ROW LEVEL SECURITY;
CREATE POLICY select_any_user_accounts ON demo_private.user_accounts FOR SELECT TO role_admin using (true);

Проблема с этим подходом состоит в том, что когда role_consumer запускает запрос, который содержит role поле

{
  me {
    firstname
    role
  }
}

Приведенный выше запрос возвращает ошибку. Это не хорошо, так как ошибка влияет на весь результат, скрывая результат других родственных полей.

2) Другой вариант - использовать защиту на уровне строк помимо уровня таблицы; IOW на уровне таблицы, чтобы предоставить выборочный доступ в таблице user_accounts обоим role_admin и role_consumer, но на уровне строк разрешить только администраторам доступ к строкам user_accounts.

GRANT USAGE ON SCHEMA demo_private TO role_consumer;
GRANT SELECT ON TABLE demo_private.user_accounts TO role_consumer;
GRANT EXECUTE ON FUNCTION demo_public.users_role(demo_public.users) TO role_consumer;
ALTER TABLE demo_private.user_accounts ENABLE ROW LEVEL SECURITY;
CREATE POLICY select_user_accounts ON demo_private.user_accounts FOR SELECT
  USING ('role_admin' = nullif(current_setting('role', true), ''));

Теперь, если пользователь с consumer_role запускает вышеупомянутый запрос, поле role будет нулевым, не затрагивая его родственные поля. Но два вопроса:

  • Должны ли мы всегда избегать ошибок, чтобы предотвратить их влияние на своих братьев и сестер?

  • Если да, должны ли мы всегда обрабатывать вещи в строке Уровень и никогда только на уровне таблицы?

1 Ответ

3 голосов
/ 26 февраля 2020

Для варианта 1 создание ошибки из PostgreSQL во время запроса не является хорошей идеей в PostGraphile, поскольку мы компилируем все дерево GraphQL в один запрос SQL, поэтому ошибка отменяет весь запрос. Вместо этого я бы включил разрешения в функцию и просто возвратил бы ноль (а не ошибку), если пользователю не разрешено его просматривать. Один из способов сделать это с помощью дополнительного предложения WHERE:

CREATE FUNCTION demo_public.users_role (
  u demo_public.users
) RETURNS TEXT AS $$
  select role
    from demo_private.user_accounts
    where user_id = u.id
    and current_setting('jwt.claims.role') = 'role_admin';
$$ LANGUAGE SQL STABLE;

Для варианта 2: это совершенно правильное решение.

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

Редко выдают ошибки при запросе вещей в GraphQL - обычно вместо этого вы возвращаете нуль. Думайте об этом, как о посещении частного репозитория на GitHub при выходе из системы - они не возвращают «запрещенную» ошибку, которая показывает, что ресурс существует, вместо этого они возвращают ошибку 404, предполагая, что это не так - если вы не знаете лучше!

Если да, должны ли мы всегда обрабатывать вещи на уровне строк, а не только на уровне таблиц?

Я лично использую только одну роль с PostGraphile, app_visitor, и это до сих пор было достаточно для всех приложений, которые я создал с PostGraphile.

...