File size: 11,336 Bytes
9b403c4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | // 在本地嵌入Postgres中验证真实SQL权限;不连接外部项目或创建实际用户。
// MESSAGE_PGLITE_MODULE可指向工作区外临时安装的@electric-sql/pglite,避免新增生产依赖。
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
const { PGlite } = await import(process.env.MESSAGE_PGLITE_MODULE || '@electric-sql/pglite')
const db = new PGlite()
const admin = '11111111-1111-4111-8111-111111111111'
const outsider = '22222222-2222-4222-8222-222222222222'
const adminSession = '33333333-3333-4333-8333-333333333333'
const outsiderSession = '44444444-4444-4444-8444-444444444444'
const keyId = '55555555-5555-4555-8555-555555555555'
const keyHash = 'a'.repeat(64)
let passed = 0
async function check(name, fn) {
await fn()
passed += 1
console.log(`通过:${name}`)
}
async function scalar(sql, params = []) {
const result = await db.query(sql, params)
return Object.values(result.rows[0])[0]
}
async function login(user = admin, session = adminSession, method = 'password') {
await db.exec('reset role; set role authenticated')
await db.query('select set_config($1,$2,false)', ['request.jwt.claims', JSON.stringify({
sub: user, session_id: session, role: 'authenticated', is_anonymous: false, amr: [{ method }],
})])
}
async function anonymous() {
await db.exec('reset role; set role anon')
await db.query('select set_config($1,$2,false)', ['request.jwt.claims', '{}'])
}
async function denied(sql, params = []) {
await assert.rejects(() => db.query(sql, params), error => error.code === '42501')
}
try {
// 仅模拟Supabase系统表的必要结构,应用的表、函数、RLS与grant来自实际迁移。
await db.exec(`
create role anon; create role authenticated; create role service_role;
create schema auth;
create table auth.users(id uuid primary key, is_anonymous boolean default false, banned_until timestamptz);
create table auth.sessions(id uuid primary key, user_id uuid, not_after timestamptz, created_at timestamptz default now());
create function auth.jwt() returns jsonb language sql stable as
'select coalesce(nullif(current_setting(''request.jwt.claims'',true),''''),''{}'')::jsonb';
create function auth.uid() returns uuid language sql stable as 'select (auth.jwt()->>''sub'')::uuid';
`)
await db.exec(await readFile(new URL('../../supabase/migrations/20261001030957_message_auth_keys.sql', import.meta.url), 'utf8'))
await db.query('insert into auth.users(id) values ($1),($2)', [admin, outsider])
await db.query('insert into auth.sessions(id,user_id) values ($1,$2),($3,$4)', [adminSession, admin, outsiderSession, outsider])
await db.query('insert into public.message_admins(user_id) values ($1)', [admin])
await check('所有Message表启用RLS', async () => {
const rows = (await db.query("select relrowsecurity from pg_class where relname in ('message_admins','message_api_keys','message_auth_audit')")).rows
assert.equal(rows.length, 3)
assert.ok(rows.every(row => row.relrowsecurity))
})
await check('public RPC无SECURITY DEFINER', async () => {
assert.equal(await scalar("select count(*)::integer from pg_proc p join pg_namespace n on p.pronamespace=n.oid where n.nspname='public' and p.proname like 'message_%' and p.prosecdef"), 0)
})
await anonymous()
await check('匿名不能读取哈希或授予管理员', async () => {
await denied('select key_hash from public.message_api_keys')
await denied('insert into public.message_admins(user_id) values ($1)', [outsider])
await denied('select public.message_current_admin()')
})
await check('匿名校验错误哈希只返回null', async () => {
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', 'invalid']), null)
})
await login(outsider, outsiderSession)
await check('普通注册用户无管理员身份与管理能力', async () => {
assert.equal(await scalar('select public.message_current_admin()'), false)
await denied('select public.message_list_api_keys($1)', ['alpha'])
await denied('insert into public.message_admins(user_id) values ($1)', [outsider])
})
await login(admin, outsiderSession)
await check('其他用户的session不能绑定管理员', async () => {
assert.equal(await scalar('select public.message_current_admin()'), false)
})
await login()
await check('显式管理员与有效session通过身份检查', async () => {
assert.equal(await scalar('select public.message_current_admin()'), true)
assert.equal(await scalar('select public.message_recovery_admin()'), false)
})
await check('管理员也不能直接读取key_hash或修改membership', async () => {
await denied('select key_hash from public.message_api_keys')
await denied('delete from public.message_admins')
})
await check('密钥创建输出不含哈希且有效期受限', async () => {
const create = 'select public.message_create_api_key($1,$2,$3,$4,$5,$6)'
const params = [keyId, 'alpha', 'fixture', 'msg_key.55555555', keyHash, 30]
const created = await scalar(create, params)
assert.equal(created.id, keyId)
assert.equal(created.expires_at - created.created_at, 30 * 86400)
assert.equal(Object.hasOwn(created, 'key_hash'), false)
assert.equal(Object.hasOwn(created, 'key'), false)
for (const days of [0, 366, null]) {
await assert.rejects(() => scalar(create, [...params.slice(0, 5), days]), error => error.code === '22023')
}
})
await check('列表只有当前profile且不泄露哈希', async () => {
const rows = await scalar('select public.message_list_api_keys($1)', ['alpha'])
assert.equal(rows.length, 1)
assert.equal(Object.hasOwn(rows[0], 'key_hash'), false)
assert.deepEqual(await scalar('select public.message_list_api_keys($1)', ['beta']), [])
})
await anonymous()
await check('匿名密钥校验只能取得绑定id/profile', async () => {
assert.deepEqual(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', keyHash]), { id: keyId, profile_id: 'alpha' })
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['beta', keyHash]), null)
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', 'b'.repeat(64)]), null)
await denied('select public.message_auth_audit()')
})
await login(admin, adminSession, 'recovery')
await check('恢复会话可核对身份但无任何管理RPC权限', async () => {
assert.equal(await scalar('select public.message_recovery_admin()'), true)
assert.equal(await scalar('select public.message_current_admin()'), false)
await denied('select public.message_list_api_keys($1)', ['alpha'])
await denied('select public.message_revoke_api_key($1,$2)', ['alpha', keyId])
await denied('select public.message_auth_audit()')
await denied('select public.message_record_auth_event($1)', ['logout'])
})
await login()
await check('封禁管理员不能通过RPC或客户端key认证', async () => {
await db.exec('reset role')
await db.query("update auth.users set banned_until=now()+interval '1 day' where id=$1", [admin])
await login()
assert.equal(await scalar('select public.message_current_admin()'), false)
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', keyHash]), null)
await db.exec('reset role')
await db.query('update auth.users set banned_until=null where id=$1', [admin])
await login()
})
await check('管理员绝对会话12小时、恢复会话10分钟上限', async () => {
await db.exec('reset role')
await db.query("update auth.sessions set created_at=now()-interval '13 hours' where id=$1", [adminSession])
await login()
assert.equal(await scalar('select public.message_current_admin()'), false)
await db.exec('reset role')
await db.query("update auth.sessions set created_at=now()-interval '11 minutes' where id=$1", [adminSession])
await login(admin, adminSession, 'recovery')
assert.equal(await scalar('select public.message_recovery_admin()'), false)
await login()
assert.equal(await scalar('select public.message_current_admin()'), true)
await db.exec('reset role')
await db.query('update auth.sessions set created_at=now() where id=$1', [adminSession])
await login()
})
await check('撤销不会跨profile且立刻阻止key验证', async () => {
assert.equal(await scalar('select public.message_revoke_api_key($1,$2)', ['beta', keyId]), false)
assert.equal(await scalar('select public.message_revoke_api_key($1,$2)', ['alpha', keyId]), true)
assert.equal(await scalar('select public.message_revoke_api_key($1,$2)', ['alpha', keyId]), false)
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', keyHash]), null)
})
await check('审计仅有固定事件且重复撤销不重复写入', async () => {
await scalar('select public.message_record_auth_event($1)', ['login.succeeded'])
await scalar('select public.message_record_auth_event($1)', ['logout'])
const rows = await scalar('select public.message_auth_audit()')
assert.equal(rows.length, 4)
assert.deepEqual(rows.map(row => row.event).sort(), ['key.created', 'key.revoked', 'login.succeeded', 'logout'])
assert.ok(rows.every(row => !Object.hasOwn(row, 'key_hash') && !Object.hasOwn(row, 'key')))
await assert.rejects(() => scalar('select public.message_record_auth_event($1)', ['untrusted-secret']), error => error.code === '22023')
})
await db.exec('reset role')
await db.query("update public.message_api_keys set revoked_at=null,created_at=now()-interval '2 days',expires_at=now()-interval '1 day' where id=$1", [keyId])
await anonymous()
await check('过期密钥拒绝认证', async () => {
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', keyHash]), null)
})
await db.exec('reset role')
await db.query("update public.message_api_keys set expires_at=now()+interval '1 day' where id=$1", [keyId])
await db.query('delete from public.message_admins where user_id=$1', [admin])
await anonymous()
await check('取消管理员membership立即禁用其客户端keys', async () => {
assert.equal(await scalar('select public.message_validate_api_key($1,$2)', ['alpha', keyHash]), null)
})
await db.exec('reset role')
await db.query('insert into public.message_admins(user_id) values($1)', [admin])
await db.query("update auth.sessions set not_after=now()-interval '1 minute' where id=$1", [adminSession])
await login()
await check('达到session截止时间拒绝管理', async () => {
assert.equal(await scalar('select public.message_current_admin()'), false)
await denied('select public.message_list_api_keys($1)', ['alpha'])
})
await db.exec('reset role')
await db.query('delete from auth.sessions where id=$1', [adminSession])
await login()
await check('已退出的session即使旧JWT尚未到期也拒绝管理', async () => {
assert.equal(await scalar('select public.message_current_admin()'), false)
await denied('select public.message_list_api_keys($1)', ['alpha'])
})
console.log(`全部${passed}项Postgres安全验证通过`)
} finally {
await db.close()
}
|