首页 > 解决方案 > 在 PostGraphile 中处理列级选择授权的最佳实践

问题描述

PostGraphile 不推荐列级 SELECT grants,而是建议

将您的关注点拆分为多个表,并使用一对一的关系功能将它们链接起来。

现在我希望我的users表有一个role可以访问role_admin但不能访问的字段role_consumer。基于上述建议,我创建了两个表。users表(在公共模式中)包含两个角色都可以看到的所有字段,并且user_accounts(在私有模式中)包含role只有role_admin必须能够看到的字段。role字段通过计算列添加到userGraphQL 类型。

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_accountsONLY 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_accountsrole_adminrole_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字段将为空,不会影响其兄弟字段。但是有两个问题:

标签: postgresqlgraphqlpostgraphile

解决方案


对于选项 1,在查询期间从 PostgreSQL 抛出错误在 PostGraphile 中不是一个好主意,因为我们将整个 GraphQL 树编译为单个 SQL 查询,因此错误会中止整个查询。相反,如果不允许用户查看权限,我会将权限考虑到函数中并简单地返回 null(而不是错误)。一种方法是使用附加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 中查询内容时很少会抛出错误 - 通常您会返回 null。可以把它想象成在注销时访问 GitHub 上的私有存储库——它们不会返回表明资源存在的“禁止”错误,而是返回 404 错误,表明它不存在——除非你知道得更好!

如果是,我们是否应该始终在行级别而不是仅在表级别处理事情?

我个人只在 PostGraphile 中使用一个角色,app_visitor这对于我迄今为止使用 PostGraphile 构建的所有应用程序来说已经足够了。


推荐阅读