在 PostGraphile 中处理列级别 select 赠款的最佳实践
Best practice to handle column level select grants in PostGraphile
PostGraphile 不推荐 column-level SELECT grants,而是推荐
split your concerns into multiple tables and use the
one-to-one relationship feature to link them.
现在我希望我的 users
table 有一个 role
字段可以被 role_admin
访问但不能被 role_consumer
访问。根据上面的推荐,我创建了两个table。 users
table(在 public 架构中)包含两个角色都可以看到的所有字段,而 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) 第一个选项是使用 table 级别的安全性。 IOW 授予 select 对 table 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
}
}
以上查询returns出错。这不好,因为错误会影响整个结果,从而隐藏其他同级字段的结果。
2) 除了 table 级别之外,另一种选择是使用行级别安全性; table 级别的 IOW,将 table user_accounts
的 select 访问权限授予 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
字段将为空,不会影响其兄弟字段。但是有两个问题:
我们是否应该始终避免错误以防止它们影响他们的兄弟姐妹?
如果是,我们是否应该始终在行级别处理事情,而不是只在 Table 级别处理?
对于选项 1,在查询期间从 PostgreSQL 抛出错误在 PostGraphile 中不是一个好主意,因为我们将整个 GraphQL 树编译成单个 SQL 查询,因此错误中止整个查询。相反,如果不允许用户查看它,我会将权限考虑到函数中并简单地 return 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:这是一个完全有效的解决方案。
Should we always avoid errors to prevent them affecting their siblings?
在 GraphQL 中查询内容时很少会抛出错误 - 通常您 return null。把它想象成在注销时访问 GitHub 上的私人存储库 - 他们不会 return 显示资源存在的 "forbidden" 错误,而是 return 404提示它不存在的错误 - 除非你知道得更多!
If yes, should we always handle things in Row Level and never only in Table Level?
我个人只在 PostGraphile 中使用一个角色,app_visitor
,这对我到目前为止用 PostGraphile 构建的所有应用程序来说已经足够了。
PostGraphile 不推荐 column-level SELECT grants,而是推荐
split your concerns into multiple tables and use the one-to-one relationship feature to link them.
现在我希望我的 users
table 有一个 role
字段可以被 role_admin
访问但不能被 role_consumer
访问。根据上面的推荐,我创建了两个table。 users
table(在 public 架构中)包含两个角色都可以看到的所有字段,而 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) 第一个选项是使用 table 级别的安全性。 IOW 授予 select 对 table 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
}
}
以上查询returns出错。这不好,因为错误会影响整个结果,从而隐藏其他同级字段的结果。
2) 除了 table 级别之外,另一种选择是使用行级别安全性; table 级别的 IOW,将 table user_accounts
的 select 访问权限授予 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
字段将为空,不会影响其兄弟字段。但是有两个问题:
我们是否应该始终避免错误以防止它们影响他们的兄弟姐妹?
如果是,我们是否应该始终在行级别处理事情,而不是只在 Table 级别处理?
对于选项 1,在查询期间从 PostgreSQL 抛出错误在 PostGraphile 中不是一个好主意,因为我们将整个 GraphQL 树编译成单个 SQL 查询,因此错误中止整个查询。相反,如果不允许用户查看它,我会将权限考虑到函数中并简单地 return 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:这是一个完全有效的解决方案。
Should we always avoid errors to prevent them affecting their siblings?
在 GraphQL 中查询内容时很少会抛出错误 - 通常您 return null。把它想象成在注销时访问 GitHub 上的私人存储库 - 他们不会 return 显示资源存在的 "forbidden" 错误,而是 return 404提示它不存在的错误 - 除非你知道得更多!
If yes, should we always handle things in Row Level and never only in Table Level?
我个人只在 PostGraphile 中使用一个角色,app_visitor
,这对我到目前为止用 PostGraphile 构建的所有应用程序来说已经足够了。