数据库授权#
数据库授权是授予和验证数据库访问权限的过程。PostgreSQL使用角色的概念管理权限。
用户和组#
角色可以被视为一个数据库用户或一组数据库用户,具体取决于角色的设置方式。
每个Web用户的角色#
PostgREST可以容纳任何一个视点。如果您将角色视为单个用户,则 基于JWT的用户模拟 做了你需要的大部分事情。当经过身份验证的用户发出请求时,PostgREST将切换到该用户的数据库角色,除了限制查询外,SQL还可以通过 current_user
变量。
您可以使用行级安全性灵活地限制当前用户的可见性和访问权限。这是一份 example 来自Tomas Vondra的聊天表,存储用户之间发送的消息。用户可以在其中插入行以向其他用户发送消息,并对其进行查询以查看其他用户发送给他们的消息。
CREATE TABLE chat (
message_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
message_time TIMESTAMP NOT NULL DEFAULT now(),
message_from NAME NOT NULL DEFAULT current_user,
message_to NAME NOT NULL,
message_subject VARCHAR(64) NOT NULL,
message_body TEXT
);
ALTER TABLE chat ENABLE ROW LEVEL SECURITY;
我们希望强制实施一项策略,以确保用户只能看到由他们发送或打算发送给他们的那些消息。此外,我们还希望防止用户伪造 message_from
列中包含另一个人的姓名。
PostgreSQL允许我们将此策略设置为具有行级安全性:
CREATE POLICY chat_policy ON chat
USING ((message_to = current_user) OR (message_from = current_user))
WITH CHECK (message_from = current_user)
访问为聊天表生成的API端点的任何人都将准确地看到他们应该看到的行,而不需要我们定制的命令式服务器端编码。
警告
角色是按群集而不是按数据库命名的,因此它们可能容易发生冲突。
Web用户共享角色#
或者,数据库角色可以表示组,而不是单个用户(或除单个用户之外)。您可以选择Web应用程序的所有登录用户共享该角色 webuser
。您可以通过在JWT中包含额外的声明(如电子邮件)来区分各个用户。
{
"role": "webuser",
"email": "john@doe.com"
}
SQL代码可以通过PostgREST访问索赔 事务作用域设置 。例如,要获取电子邮件声明,请调用此函数:
current_setting('request.jwt.claims', true)::json->>'email';
备注
对于PostgreSQL<14
current_setting('request.jwt.claim.email', true);
这允许JWT生成服务包含额外的信息,并允许您的数据库代码对其做出反应。例如,可以将RLS示例修改为使用以下内容 current_setting
而不是 current_user
。第二 'true'
论证告诉我们 current_setting
如果当前配置中缺少该设置,则返回NULL。
混合用户-组角色#
您可以混合使用组角色策略和个人角色策略。例如,我们仍然可以拥有一个网络管理员角色和从该角色继承的个人用户:
CREATE ROLE webuser NOLOGIN;
-- grant this role access to certain tables etc
CREATE ROLE user000 NOLOGIN;
GRANT webuser TO user000;
-- now user000 can do whatever webuser can
GRANT user000 TO authenticator;
-- allow authenticator to switch into user000 role
-- (the role itself has nologin)
模式#
必须显式允许角色访问中公开的架构 DB-架构 。
GRANT USAGE ON SCHEMA api TO webuser;
表格#
要允许Web用户访问表,您必须授予他们执行您希望他们执行的操作的权限。
GRANT
SELECT
, INSERT
, UPDATE(message_body)
, DELETE
ON chat TO webuser;
您还可以选择对哪些表列执行有效的操作。在上面的示例中,Web用户只能更新 message_body
纵队。
功能#
默认情况下,创建函数时,执行该函数的权限不受角色的限制。函数访问是 PUBLIC
-可由所有角色执行(更多详细信息请参阅 PostgreSQL Privileges page )。这对于API架构来说并不理想。要禁用此行为,可以运行以下SQL语句:
ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
这将更改将来在所有模式中创建的所有函数的权限。目前还没有办法将其限制在单个模式中。在我们看来,这无论如何都是一种很好的做法。
备注
但是,可以将该条款的效果仅限于您定义的函数。您可以将上述语句放在API模式定义的开头,然后在结尾处使用以下命令反转:
ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO PUBLIC;
这将是可行的,因为 alter default privileges
语句对创建的函数有效 after 它被执行了。看见 PostgreSQL alter default privileges 了解更多详细信息。
在此之后,您需要显式授予函数的EXECUTE权限:
GRANT EXECUTE ON FUNCTION login TO anonymous;
GRANT EXECUTE ON FUNCTION signup TO anonymous;
您还可以将架构中所有函数的EXECUTE权限授予更高权限的角色:
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA api TO web_user;
安全定义者#
函数是以调用它的用户的权限执行的。这意味着用户必须拥有执行该函数执行的操作的所有权限。如果该函数访问私有数据库对象,则您的 API roles 将无法成功执行该函数。
另一种选择是使用 SECURITY DEFINER
选择。那么只会进行一次权限检查,即调用函数的权限,并且函数中的操作将拥有拥有函数本身的用户的权限。
-- login as a user wich has privileges on the private schemas
-- create a sample function
create or replace function login(email text, pass text, out token text) as $$
begin
-- access to a private schema called 'auth'
select auth.user_role(email, pass) into _role;
-- other operations
-- ...
end;
$$ language plpgsql security definer;
请注意 SECURITY DEFINER
函数结尾处的关键字。看见 PostgreSQL documentation 了解更多详细信息。
视图#
视图是使用视图所有者的权限调用的,这与使用 SECURITY DEFINER
选择。由超级用户角色创建时,所有 row-level security 政策将被绕过。
如果您使用的是PostgreSQL>=15,则可以通过指定 security_invoker
选择。
CREATE VIEW sample_view WITH (security_invoker = true) AS
SELECT * FROM sample_table;
在PostgreSQL<15上,您可以创建一个非超级用户角色,并使该角色成为视图的所有者。
CREATE ROLE api_views_owner NOSUPERUSER NOBYPASSRLS;
ALTER VIEW sample_view OWNER TO api_views_owner;