为外键属性的行级安全性创建 Postgres 策略
Create Postgres Policy for Row Level Security on foreign key attribute
我正在尝试在 Postgres 上创建策略以启用行级安全性,但我遇到了一些麻烦。我发现的许多示例在 table 上都有一个直接的 FK
,但我试图在外部 table.
上的一个属性上这样做
这里的上下文是我试图让用户只能看到来自同一组织的其他用户。用户在配置参数 organization.current_tenant
上设置,该参数采用 Organization.key
的值
设置:
CREATE TABLE organization (id serial primary key, name text, key text);
CREATE TABLE myuser (id serial primary key, name text, user_organization_id integer);
INSERT INTO organization (name, key) values ('org1', 'org1-key');
INSERT INTO organization (name, key) values ('org2', 'org2-key');
INSERT INTO myuser (name, user_organization_id) values ('org1_agent1', 1);
INSERT INTO myuser (name, user_organization_id) values ('org2_agent1', 2);
INSERT INTO myuser (name, user_organization_id) values ('org2_agent2', 2);
ALTER TABLE myuser ENABLE ROW LEVEL SECURITY;
ALTER TABLE myuser FORCE ROW LEVEL SECURITY;
直接使用时 FK
, organization_id
:
CREATE POLICY access_tenant_data ON myuser
USING (user_organization_id::TEXT = current_setting('organization.current_tenant'));
这是可行的,因为 user_organization_id
是 user
table 上可用的外键。
但是,我想使用的是 user.organization.key
.
CREATE POLICY access_tenant_data ON myuser
USING (
key IN (
SELECT key FROM
myuser U INNER JOIN organization O
ON U.organization_id = O.id
)::TEXT = current_setting('organization.current_tenant')
);
但这显然行不通。我不确定如何从这里开始。
正在测试:
SET organization.current_tenant = "org1-key";
SELECT * from myuser;
您需要定义一个return布尔型函数。
在该函数中,您将确定逻辑和 return TRUE 或 FALSE。
重要的一点是:策略的 USING 子句需要一个 BOOLEAN 值,我们可以根据函数 return 得到的内容 - True 或 False 来决定策略 (allow/disallow) 的行为。
这是要在策略中使用的示例 FUNCTION:
create function KeyExist(organization_id int, env_current_tenant text) returns boolean
language plpgsql
as
$$
declare
flag boolean;
keyValue text;
begin
select key into keyValue from organization where id=organization_id;
if not found then
flag := false;
else
if keyValue = env_current_tenant then
flag := true;
else
flag := false;
end if;
end if;
return flag;
end; $$;
政策:
CREATE POLICY access_tenant_data ON myuser for select
USING (KeyExist(user_organization_id,current_setting('organization.current_tenant')));
演示:
postgres=# create function KeyExist(organization_id int, env_current_tenant text) returns boolean
postgres-# language plpgsql
postgres-# as
postgres-# $$
postgres$# declare
postgres$# flag boolean;
postgres$# keyValue text;
postgres$# begin
postgres$# select key into keyValue from organization where id=organization_id;
postgres$# if not found then
postgres$# flag := false;
postgres$# else
postgres$# if keyValue = env_current_tenant then
postgres$#
postgres$# flag := true;
postgres$# else
postgres$#
postgres$# flag := false;
postgres$# end if;
postgres$# end if;
postgres$# return flag;
postgres$# end; $$;
CREATE FUNCTION
postgres=# CREATE POLICY access_tenant_data ON myuser for select
postgres-# USING (KeyExist(user_organization_id,current_setting('organization.current_tenant')));
CREATE POLICY
验证:
postgres=> SET organization.current_tenant = "org1-key";
SET
postgres=> select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
1 | org1_agent1 | 1
(1 row)
postgres=> SET organization.current_tenant = 'org2-key';
SET
postgres=> select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
2 | org2_agent1 | 2
3 | org2_agent2 | 2
(2 rows)
表:
postgres=# \d myuser
Table "public.myuser"
Column | Type | Collation | Nullable | Default
----------------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('myuser_id_seq'::regclass)
name | text | | |
user_organization_id | integer | | |
Indexes:
"myuser_pkey" PRIMARY KEY, btree (id)
Policies (forced row security enabled):
POLICY "access_tenant_data" FOR SELECT
USING (keyexist(user_organization_id, current_setting('organization.current_tenant'::text)))
postgres=# select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
1 | org1_agent1 | 1
2 | org2_agent1 | 2
3 | org2_agent2 | 2
(3 rows)
postgres=# select * from organization;
id | name | key
----+------+----------
1 | org1 | org1-key
2 | org2 | org2-key
(2 rows)
这应该很简单:
CREATE POLICY access_tenant_data ON myuser
USING (
EXISTS (SELECT 1 FROM organization AS o
WHERE myuser.user_organization_id = o.id
AND o.key = current_setting('organization.current_tenant')
)
);
这可能相当有效,因为 PostgreSQL 可以使用 semi-join.
计算结果查询
我正在尝试在 Postgres 上创建策略以启用行级安全性,但我遇到了一些麻烦。我发现的许多示例在 table 上都有一个直接的 FK
,但我试图在外部 table.
这里的上下文是我试图让用户只能看到来自同一组织的其他用户。用户在配置参数 organization.current_tenant
上设置,该参数采用 Organization.key
设置:
CREATE TABLE organization (id serial primary key, name text, key text);
CREATE TABLE myuser (id serial primary key, name text, user_organization_id integer);
INSERT INTO organization (name, key) values ('org1', 'org1-key');
INSERT INTO organization (name, key) values ('org2', 'org2-key');
INSERT INTO myuser (name, user_organization_id) values ('org1_agent1', 1);
INSERT INTO myuser (name, user_organization_id) values ('org2_agent1', 2);
INSERT INTO myuser (name, user_organization_id) values ('org2_agent2', 2);
ALTER TABLE myuser ENABLE ROW LEVEL SECURITY;
ALTER TABLE myuser FORCE ROW LEVEL SECURITY;
直接使用时 FK
, organization_id
:
CREATE POLICY access_tenant_data ON myuser
USING (user_organization_id::TEXT = current_setting('organization.current_tenant'));
这是可行的,因为 user_organization_id
是 user
table 上可用的外键。
但是,我想使用的是 user.organization.key
.
CREATE POLICY access_tenant_data ON myuser
USING (
key IN (
SELECT key FROM
myuser U INNER JOIN organization O
ON U.organization_id = O.id
)::TEXT = current_setting('organization.current_tenant')
);
但这显然行不通。我不确定如何从这里开始。
正在测试:
SET organization.current_tenant = "org1-key";
SELECT * from myuser;
您需要定义一个return布尔型函数。 在该函数中,您将确定逻辑和 return TRUE 或 FALSE。 重要的一点是:策略的 USING 子句需要一个 BOOLEAN 值,我们可以根据函数 return 得到的内容 - True 或 False 来决定策略 (allow/disallow) 的行为。
这是要在策略中使用的示例 FUNCTION:
create function KeyExist(organization_id int, env_current_tenant text) returns boolean
language plpgsql
as
$$
declare
flag boolean;
keyValue text;
begin
select key into keyValue from organization where id=organization_id;
if not found then
flag := false;
else
if keyValue = env_current_tenant then
flag := true;
else
flag := false;
end if;
end if;
return flag;
end; $$;
政策:
CREATE POLICY access_tenant_data ON myuser for select
USING (KeyExist(user_organization_id,current_setting('organization.current_tenant')));
演示:
postgres=# create function KeyExist(organization_id int, env_current_tenant text) returns boolean
postgres-# language plpgsql
postgres-# as
postgres-# $$
postgres$# declare
postgres$# flag boolean;
postgres$# keyValue text;
postgres$# begin
postgres$# select key into keyValue from organization where id=organization_id;
postgres$# if not found then
postgres$# flag := false;
postgres$# else
postgres$# if keyValue = env_current_tenant then
postgres$#
postgres$# flag := true;
postgres$# else
postgres$#
postgres$# flag := false;
postgres$# end if;
postgres$# end if;
postgres$# return flag;
postgres$# end; $$;
CREATE FUNCTION
postgres=# CREATE POLICY access_tenant_data ON myuser for select
postgres-# USING (KeyExist(user_organization_id,current_setting('organization.current_tenant')));
CREATE POLICY
验证:
postgres=> SET organization.current_tenant = "org1-key";
SET
postgres=> select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
1 | org1_agent1 | 1
(1 row)
postgres=> SET organization.current_tenant = 'org2-key';
SET
postgres=> select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
2 | org2_agent1 | 2
3 | org2_agent2 | 2
(2 rows)
表:
postgres=# \d myuser
Table "public.myuser"
Column | Type | Collation | Nullable | Default
----------------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('myuser_id_seq'::regclass)
name | text | | |
user_organization_id | integer | | |
Indexes:
"myuser_pkey" PRIMARY KEY, btree (id)
Policies (forced row security enabled):
POLICY "access_tenant_data" FOR SELECT
USING (keyexist(user_organization_id, current_setting('organization.current_tenant'::text)))
postgres=# select * from myuser;
id | name | user_organization_id
----+-------------+----------------------
1 | org1_agent1 | 1
2 | org2_agent1 | 2
3 | org2_agent2 | 2
(3 rows)
postgres=# select * from organization;
id | name | key
----+------+----------
1 | org1 | org1-key
2 | org2 | org2-key
(2 rows)
这应该很简单:
CREATE POLICY access_tenant_data ON myuser
USING (
EXISTS (SELECT 1 FROM organization AS o
WHERE myuser.user_organization_id = o.id
AND o.key = current_setting('organization.current_tenant')
)
);
这可能相当有效,因为 PostgreSQL 可以使用 semi-join.
计算结果查询