101 lines
5.1 KiB
Markdown
101 lines
5.1 KiB
Markdown
# B 档落地 · 插件建库权限(角色级授权 `CREATEDB`)
|
||||
|
|
|
|||
|
|
> **拍板**:2026-09-23 用户原话「**B**」⇒ 建库通路 = **B 档(给 `dshs` 角色加 `CREATEDB`)**。
|
|||
|
|
> **状态**:✅ **已在 47 落地并通过 11 条验收**(2026-09-23 05:4x)。
|
|||
|
|
> **机位**:仅 **47**(PG 只在 Manager 上;106 无 PG)。
|
|||
|
|
> **前置件**:`交付物/建库权限-D档被证伪与修正判定-20260923.md`(本档为何取代 D 档)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一 拍板内容与落地动作
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
ALTER ROLE dshs CREATEDB; -- 唯一一句 DDL(幂等)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
平台侧建库 = 用**现有** `dshs` 连接、**单语句**执行:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
CREATE DATABASE dshs_pl_<pluginId> OWNER dshs ENCODING 'UTF8' TEMPLATE template0;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
🔴 **必须单语句、不走事务** —— `CREATE DATABASE` 不能在事务块内(这是 D 档被证伪的同一条 PG 限制,B 档同样受约束,只是不再需要函数封装)。平台侧用 `pg` 的**非事务**执行路径(顶层 `client.query('CREATE DATABASE …')`,⛔ 不要包在 `BEGIN/COMMIT` 里)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二 验收(11 条 · 实测读数)
|
|||
|
|
|
|||
|
|
| # | 断言 | 实测 | 判定 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| ① | `dshs` 可建库 | 建库成功 | ✅ |
|
|||
|
|
| ② | 重名建库被拒(幂等靠错误码捕获) | `ERROR: database … already exists` | ✅ |
|
|||
|
|
| ③ | **新库属主 = `dshs`** | `dshs_pl_bselftest/dshs` | ✅ |
|
|||
|
|
| ④ | 可连新库并建表 | `create table` 成功 | ✅ |
|
|||
|
|
| ⑤ | 新库内对象归 `dshs` | 表数 = `1` | ✅ |
|
|||
|
|
| ⑥ | **权限精确**(须 `f\|t\|f`) | `false\|true\|false` | ✅ |
|
|||
|
|
| ⑦ | ⛔ 未拿到 `CREATEROLE` | `t`(= 无该权) | ✅ |
|
|||
|
|
| ⑧ | ⛔ 仍非超级用户 | `t`(= 非超管) | ✅ |
|
|||
|
|
| ⑨ | 库名白名单 | **PG 侧零拦截**(建 `evil_probe` 成功) | ⚠️ 见 §三 |
|
|||
|
|
| ⑩ | 内核表完好 | `13` | ✅ |
|
|||
|
|
| ⑪ | 现有库未损 | `2`(`dshs` + `postgres`) | ✅ |
|
|||
|
|
| — | 清理后残留 | `0` | 服务 `dshs = active` | ✅ |
|
|||
|
|
|
|||
|
|
⑥ 是本档的**核心断言**:只多出 `CREATEDB` 一位,`rolsuper` / `rolcreaterole` 必须保持 `false`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三 🔴 本档的已知代价(必须由平台代码兜住)
|
|||
|
|
|
|||
|
|
⑨ 揭示了 B 档的真实缺口:**PG 侧没有"只允许建 `dshs_pl_*` 前缀库"的原生机制** ⇒ 白名单**只能靠平台代码自觉**。这正是 09-22 否掉 A 档的同一条理由,B 档原样继承。
|
|||
|
|
|
|||
|
|
**因此平台侧必须强制三点**(缺一即为该档的漏洞面):
|
|||
|
|
|
|||
|
|
1. **库名单点生成** —— 库名只能来自 `.dsh` 声明的换算函数(包名去 scope、小写、`-`→`_`、前缀 `dshs_pl_`),⛔ 不接受外部传入的任意串。
|
|||
|
|
2. **建库前正则复校** —— 照抄 D 档那条:`^dshs_pl_[a-z][a-z0-9_]{0,40}$`,不匹配直接 400,**⛔ 不把变量拼进 SQL 字符串**(用标识符引用或参数化)。
|
|||
|
|
3. **每次建库落台账 + 审计** —— 写 `plugin_datastores`(库名 / 插件 ID / 建库时间 / 结果),使"平台到底建过哪些库"可对账;`DROP` 须 admin 二次确认(`DB-03 §六-4`)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四 回滚
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
ALTER ROLE dshs NOCREATEDB; -- 一句话回滚
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
影响面:**已建好的 `dshs_pl_*` 库不受影响**(属主是 `dshs`,平台照常建表读写)⇒ 只是新插件建不了库。回滚点 = 平台建库口返回 `PG_CREATEDB_MISSING`(503)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五 ⚠️ 必须同步落的文档(回滚/重建时不能漏)
|
|||
|
|
|
|||
|
|
1. **`dsh-server-docs/DEPLOY-本部署.md`** DB 初始化步骤追加:`ALTER ROLE dshs CREATEDB;`(标 🔴 用途 = 插件建库;⛔ 不得顺手给 `CREATEROLE` / `SUPERUSER`)。
|
|||
|
|
2. **`交接单/插件投放与分库线-①…md`** —— `§五 S4-2` 建库 = **admin 显式按钮**;`§十-D1` 原文「上传钩子建库」须同步改。
|
|||
|
|
3. **`DB-03-插件数据面规范.md`** —— `§三`(谁建库)/ `§四`(何时建)/ `§六`(台账表 `plugin_datastores`)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六 平台侧代码接口
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
// src/db/plugin-data/datastore.ts
|
|||
|
|
export function pluginDbNameOf(pkgName: string): string // 纯函数:包名 → dshs_pl_*
|
|||
|
|
export function assertPluginDbName(dbName: string): void // ^dshs_pl_[a-z][a-z0-9_]{0,40}$
|
|||
|
|
|
|||
|
|
export async function createPluginDatabase(client: PoolClient, dbName: string): Promise<'created' | 'exists'>
|
|||
|
|
// 错误映射(供路由层用):
|
|||
|
|
// PG 42P04 duplicate_database ⇒ 'exists'(幂等,非错误)
|
|||
|
|
// PG 42501 权限不足 ⇒ 503 PG_CREATEDB_MISSING(未授 CREATEDB / 已回滚)
|
|||
|
|
// 名称不合白名单 ⇒ 400 invalid_plugin_db_name(平台侧复校,⛔ 不下发到 PG)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 错误码变更:D 档的 `PLUGIN_DB_HELPER_MISSING` 随档位**作废**,恢复为 `PG_CREATEDB_MISSING`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七 未做(具名原因)
|
|||
|
|
|
|||
|
|
| 项 | 原因 |
|
|||
|
|
|---|---|
|
|||
|
|
| 平台侧代码实现(§六) | 属交接单 §五 S4 的**代码面**,按用户拍板的「A 档排期」= 等 IM 线整阶段收官后一次性跑完 |
|
|||
|
|
| §五 三份文档回写 | 本轮落点已定,但改交接单/DB-03 会与他线**共享文件**冲突;按 R9 拉长到同一批执行 |
|
|||
|
|
| `drop_plugin_database` | ⛔ 不在本次拍板范围(`DB-03 §六-4` 将来一并走) |
|