# 第 13 章 配套代码 · 权限与安全

## 准备工作

1. 跑 `psql -h 127.0.0.1 -U postgres -d learn_pg -f ../init.sql` 初始化测试表与角色
2. 安装依赖：`pip install "psycopg[binary]>=3.1"`
3. （可选）通过环境变量覆盖默认连接：`export PG_DSN="host=... port=... dbname=... user=..."`
4. 本章脚本默认以 `postgres` 超级用户连接。运行 03 时还会以 `tenant_a / tenant_b / dba_user` 三个角色登录，密码已写在 `init.sql`。

## 脚本一览（推荐运行顺序）

| 脚本 | 一句话说明 | 关键 PG 特性 |
|------|------------|--------------|
| `01_role_grant.py` | 演示 ROLE 创建、组继承、`GRANT/REVOKE` 全流程，最后清理 | `CREATE ROLE`、`INHERIT`、`SET ROLE` |
| `02_default_privileges.py` | 验证 `ALTER DEFAULT PRIVILEGES` 让未来对象也自动授权 | `ALTER DEFAULT PRIVILEGES`、PUBLIC 收权 |
| `03_rls_multi_tenant.py` | 多租户 RLS 隔离，跨租户写入被 `WITH CHECK` 拦截 | RLS、`USING / WITH CHECK`、`FORCE RLS`、`BYPASSRLS` |
| `04_pg_hba_demo.py` | 解析当前连接的认证元信息，列举 `pg_hba.conf` 优先级与常见报错 | `pg_hba.conf`、`scram-sha-256`、`pg_hba_file_rules` |

## 预期输出

`03_rls_multi_tenant.py` 关键片段（命中 RLS 后只能看到自己 tenant 的工单）：

```
============================================================
Step 1：以两个租户身份分别 SELECT，应该只看到自己的行
============================================================

--- 以 tenant_a 身份连接 (tenant=1) ---
    id=1   tenant=1 status=open     title=租户A-工单001
    id=2   tenant=1 status=closed   title=租户A-工单002

--- 以 tenant_b 身份连接 (tenant=2) ---
    id=4   tenant=2 status=open     title=租户B-工单001
    ...
```

注意：`init.sql` 写入了一行 `archived` 工单，但 `ch13_no_archived` 的 RESTRICTIVE 策略会让它对所有人不可见。

## 常见报错与依赖

- `connection refused` → PG 未启动，或 `pg_hba.conf` 没放行本机
- `relation "ch13_tickets" does not exist` → 未运行 `init.sql`
- `password authentication failed for user "tenant_a"` → 改过密码后忘记同步脚本里的密码常量
- `permission denied for table ch13_tickets` → 当前角色不在 `devs` 组里，回到 `init.sql` 检查 `GRANT devs TO ...`
- `04_pg_hba_demo.py` 仅做只读检查，不会改 `pg_hba.conf`；如需修改请直接编辑 `$PGDATA/pg_hba.conf` 后 `SELECT pg_reload_conf();`
