【发布时间】:2021-02-04 04:56:09
【问题描述】:
我正在考虑使用我们的 node express + pg-promise + postgres 服务来实现行级安全性。
我们尝试了几种方法都没有成功:
- 创建一个 getDb(tenantId) 包装器,它在返回 db 对象之前调用 SET app.current_tenant = '${tenantId}';` sql 语句
- getDb(tenantId) 包装器,每次都获取一个新的 db 对象 - 这适用于一些请求,但最终会导致过多的 db 连接和错误输出(这是可以理解的,因为它没有使用 pg-promise 的连接池管理)
- getDb(tenantId) 包装器,它使用名称值(映射)来存储每个租户的数据库连接列表。这会暂时有效,但最终会导致数据库连接过多)。
- 利用 initOptions > connect 事件 - 尚未找到获取当前请求对象的方法(然后设置tenant_id)
有人可以(希望是vitaly-t :))建议在连接内运行所有sql查询之前注入当前租户的最佳策略。
非常感谢
这是一个简短的代码示例:
const promise = require('bluebird');
const initOptions = {
promiseLib: promise,
connect: async (client, dc, useCount) => {
try {
// "hook" into the db connect event - and set the tenantId so all future sql queries in this connection
// have an implied WHERE tenant_id = app.current_setting('app.current_tenant')::UUID (aka PostGres Row Level Security)
const tenantId = client.$ctx?.cn?.tenantId || client.$ctx?.cnOptions?.tenantId;
if (tenantId) {
await client.query(`SET app.current_tenant = '${tenantId}';`);
}
} catch (ex) {
log.error('error in db.js initOptions', {ex});
}
}
};
const pgp = require('pg-promise')(initOptions);
const options = tenantIdOptional => {
return {
user: process.env.POSTGRES_USER,
host: process.env.POSTGRES_HOST,
database: process.env.POSTGRES_DATABASE,
password: process.env.POSTGRES_PASSWORD,
port: process.env.POSTGRES_PORT,
max: 100,
tenantId: tenantIdOptional
};
};
const db = pgp(options());
const getDb = tenantId => {
// how to inject tenantId into the db object
// 1. this was getting an error "WARNING: Creating a duplicate database object for the same connection and Error: write EPIPE"
// const tmpDb = pgp(options(tenantId));
// return tmpDb;
// 2. this was running the set app.current_tenant BEFORE the database connection was established
// const setTenantId = async () => {
// await db.query(`SET app.current_tenant = '${tenantId}';`);
// };
// setTenantId();
// return db;
// 3. this is bypassing the connection pool management - and is not working
// db.connect(options(tenantId));
// return db;
return db;
};
// Exporting the global database object for shared use:
const exportFunctions = {
getDb,
db // have to also export db for the legacy non-Row level security areas of the service
};
module.exports = exportFunctions;
【问题讨论】:
-
啊,这么多的请求,我刚刚开始向
xcode移动,在这些困难时期尝试从中赚取至少一些钱。但我会尽量抽出时间在这里回答:)如果很紧急,您可以尝试GitHub项目页面上的sponsor按钮。我刚刚设置,还没用,所以你会是第一个:) -
我的回答没有帮助吗?如果是,请接受。
-
嘿,vitally-t。太感谢了。很抱歉没有早点回应 - 一直忙于实施变更。您的回复证实了我们使用任务/txs 的方法。而且,是的,我会把我的老板引向赞助商按钮。我们非常感谢您的出色工作。保持安全和健康的伴侣
标签: pg-promise row-level-security