【问题标题】:Best practice to handle column level select grants in PostGraphile在 PostGraphile 中处理列级选择授权的最佳实践
【发布时间】:2020-06-09 16:10:30
【问题描述】:

PostGraphile 不推荐column-level SELECT grants,而是推荐

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

现在我希望我的users 表有一个role 字段,role_admin 可以访问但role_consumer 不能访问。基于上述建议,我创建了两个表。 users 表(在公共模式中)包含两个角色都可以看到的所有字段,user_accounts(在私有模式中)包含只有role_admin 必须能够看到的role 字段。 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_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 字段将为空,不会影响其兄弟字段。但是有两个问题:

  • 我们是否应该始终避免错误以防止它们影响兄弟姐妹?

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

【问题讨论】:

    标签: postgresql graphql postgraphile


    【解决方案1】:

    对于选项 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 构建的所有应用程序来说已经足够了。

    【讨论】:

    • 谢谢@Benjie。对于最后一个问题,如果只有一个角色还不够怎么办。许多组织的用户具有不同的角色和对可用资源的不同访问级别。我想如果我们想避免错误,我们总是先授予表级权限,然后通过 RLS 处理,以避免权限错误。对吗?
    • 我使用 RLS 而不是 PostgreSQL 角色管理组织角色。 RLS 策略根据您对用户的了解(成员资格、访问级别等)来管理用户可以更新哪些行。我也经常使用自定义突变。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 2020-03-20
    • 1970-01-01
    • 2021-09-26
    • 2021-07-13
    • 1970-01-01
    相关资源
    最近更新 更多