column-privileges.test.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. import { afterAll, beforeAll, expect, test } from 'vitest'
  2. import pgMeta from '../src/index'
  3. import { cleanupRoot, createTestDatabase } from './db/utils'
  4. beforeAll(async () => {
  5. // Any global setup if needed
  6. })
  7. afterAll(async () => {
  8. await cleanupRoot()
  9. })
  10. const withTestDatabase = (
  11. name: string,
  12. fn: (db: Awaited<ReturnType<typeof createTestDatabase>>) => Promise<void>
  13. ) => {
  14. test(name, async () => {
  15. const db = await createTestDatabase()
  16. try {
  17. await fn(db)
  18. } finally {
  19. await db.cleanup()
  20. }
  21. })
  22. }
  23. withTestDatabase('list column privileges', async ({ executeQuery }) => {
  24. const { sql, zod } = await pgMeta.columnPrivileges.list()
  25. const res = zod.parse(await executeQuery(sql))
  26. const column = res.find(
  27. ({ relation_schema, relation_name, column_name }) =>
  28. relation_schema === 'public' && relation_name === 'todos' && column_name === 'id'
  29. )!
  30. // We don't guarantee order of privileges, but we want to keep the snapshots consistent.
  31. column.privileges.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)))
  32. expect(column).toMatchInlineSnapshot(
  33. { column_id: expect.stringMatching(/^\d+\.\d+$/) },
  34. `
  35. {
  36. "column_id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
  37. "column_name": "id",
  38. "privileges": [
  39. {
  40. "grantee": "postgres",
  41. "grantor": "postgres",
  42. "is_grantable": false,
  43. "privilege_type": "INSERT",
  44. },
  45. {
  46. "grantee": "postgres",
  47. "grantor": "postgres",
  48. "is_grantable": false,
  49. "privilege_type": "REFERENCES",
  50. },
  51. {
  52. "grantee": "postgres",
  53. "grantor": "postgres",
  54. "is_grantable": false,
  55. "privilege_type": "SELECT",
  56. },
  57. {
  58. "grantee": "postgres",
  59. "grantor": "postgres",
  60. "is_grantable": false,
  61. "privilege_type": "UPDATE",
  62. },
  63. ],
  64. "relation_name": "todos",
  65. "relation_schema": "public",
  66. }
  67. `
  68. )
  69. })
  70. withTestDatabase('revoke & grant column privileges', async ({ executeQuery }) => {
  71. const testRole = `test_role_${Date.now()}`
  72. // Create test role
  73. await executeQuery(`create role ${testRole};`)
  74. // Get initial column privileges
  75. const { sql: listSql, zod: listZod } = await pgMeta.columnPrivileges.list()
  76. const listRes = listZod.parse(await executeQuery(listSql))
  77. const { column_id } = listRes.find(
  78. ({ relation_schema, relation_name, column_name }) =>
  79. relation_schema === 'public' && relation_name === 'todos' && column_name === 'id'
  80. )!
  81. const { sql: listSqlTodos } = await pgMeta.columnPrivileges.list({ columnIds: [column_id] })
  82. // Grant all privileges
  83. const { sql: grantSql } = pgMeta.columnPrivileges.grant([
  84. {
  85. columnId: column_id,
  86. grantee: testRole,
  87. privilegeType: 'ALL',
  88. },
  89. ])
  90. await executeQuery(grantSql)
  91. let privs = listZod.parse(await executeQuery(listSqlTodos))
  92. expect(privs.length).toBe(1)
  93. expect(privs[0]).toMatchInlineSnapshot(
  94. { column_id: expect.stringMatching(/^\d+\.\d+$/) },
  95. `
  96. {
  97. "column_id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
  98. "column_name": "id",
  99. "privileges": [
  100. {
  101. "grantee": "${testRole}",
  102. "grantor": "postgres",
  103. "is_grantable": false,
  104. "privilege_type": "UPDATE",
  105. },
  106. {
  107. "grantee": "${testRole}",
  108. "grantor": "postgres",
  109. "is_grantable": false,
  110. "privilege_type": "SELECT",
  111. },
  112. {
  113. "grantee": "${testRole}",
  114. "grantor": "postgres",
  115. "is_grantable": false,
  116. "privilege_type": "REFERENCES",
  117. },
  118. {
  119. "grantee": "${testRole}",
  120. "grantor": "postgres",
  121. "is_grantable": false,
  122. "privilege_type": "INSERT",
  123. },
  124. {
  125. "grantee": "postgres",
  126. "grantor": "postgres",
  127. "is_grantable": false,
  128. "privilege_type": "UPDATE",
  129. },
  130. {
  131. "grantee": "postgres",
  132. "grantor": "postgres",
  133. "is_grantable": false,
  134. "privilege_type": "SELECT",
  135. },
  136. {
  137. "grantee": "postgres",
  138. "grantor": "postgres",
  139. "is_grantable": false,
  140. "privilege_type": "REFERENCES",
  141. },
  142. {
  143. "grantee": "postgres",
  144. "grantor": "postgres",
  145. "is_grantable": false,
  146. "privilege_type": "INSERT",
  147. },
  148. ],
  149. "relation_name": "todos",
  150. "relation_schema": "public",
  151. }
  152. `
  153. )
  154. // Revoke all privileges
  155. const { sql: revokeSql } = pgMeta.columnPrivileges.revoke([
  156. {
  157. columnId: column_id,
  158. grantee: testRole,
  159. privilegeType: 'ALL',
  160. },
  161. ])
  162. await executeQuery(revokeSql)
  163. // Verify privileges were revoked
  164. privs = listZod.parse(await executeQuery(listSqlTodos))
  165. expect(privs.length).toBe(1)
  166. expect(privs[0]).toMatchInlineSnapshot(
  167. { column_id: expect.stringMatching(/^\d+\.\d+$/) },
  168. `
  169. {
  170. "column_id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
  171. "column_name": "id",
  172. "privileges": [
  173. {
  174. "grantee": "postgres",
  175. "grantor": "postgres",
  176. "is_grantable": false,
  177. "privilege_type": "UPDATE",
  178. },
  179. {
  180. "grantee": "postgres",
  181. "grantor": "postgres",
  182. "is_grantable": false,
  183. "privilege_type": "SELECT",
  184. },
  185. {
  186. "grantee": "postgres",
  187. "grantor": "postgres",
  188. "is_grantable": false,
  189. "privilege_type": "REFERENCES",
  190. },
  191. {
  192. "grantee": "postgres",
  193. "grantor": "postgres",
  194. "is_grantable": false,
  195. "privilege_type": "INSERT",
  196. },
  197. ],
  198. "relation_name": "todos",
  199. "relation_schema": "public",
  200. }
  201. `
  202. )
  203. })
  204. withTestDatabase(
  205. 'revoke & grant column privileges w/ quoted column name',
  206. async ({ executeQuery }) => {
  207. const testRole = `test_role_${Date.now()}`
  208. // Create test role and table with quoted names
  209. await executeQuery(`create role ${testRole}; create table "t 1"("c 1" int8);`)
  210. // Get column privileges
  211. const { sql: listSql, zod: listZod } = await pgMeta.columnPrivileges.list()
  212. const listRes = listZod.parse(await executeQuery(listSql))
  213. const { column_id } = listRes.find(
  214. ({ relation_name, column_name }) => relation_name === 't 1' && column_name === 'c 1'
  215. )!
  216. const { sql: listSqlT1 } = await pgMeta.columnPrivileges.list({ columnIds: [column_id] })
  217. // Grant all privileges
  218. const { sql: grantSql } = pgMeta.columnPrivileges.grant([
  219. {
  220. columnId: column_id,
  221. grantee: testRole,
  222. privilegeType: 'ALL',
  223. },
  224. ])
  225. await executeQuery(grantSql)
  226. // Verify privileges were granted
  227. let privs = listZod.parse(await executeQuery(listSqlT1))
  228. expect(privs.length).toBe(1)
  229. expect(privs[0]).toMatchInlineSnapshot(
  230. { column_id: expect.stringMatching(/^\d+\.\d+$/) },
  231. `
  232. {
  233. "column_id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
  234. "column_name": "c 1",
  235. "privileges": [
  236. {
  237. "grantee": "${testRole}",
  238. "grantor": "postgres",
  239. "is_grantable": false,
  240. "privilege_type": "UPDATE",
  241. },
  242. {
  243. "grantee": "${testRole}",
  244. "grantor": "postgres",
  245. "is_grantable": false,
  246. "privilege_type": "SELECT",
  247. },
  248. {
  249. "grantee": "${testRole}",
  250. "grantor": "postgres",
  251. "is_grantable": false,
  252. "privilege_type": "REFERENCES",
  253. },
  254. {
  255. "grantee": "${testRole}",
  256. "grantor": "postgres",
  257. "is_grantable": false,
  258. "privilege_type": "INSERT",
  259. },
  260. {
  261. "grantee": "postgres",
  262. "grantor": "postgres",
  263. "is_grantable": false,
  264. "privilege_type": "UPDATE",
  265. },
  266. {
  267. "grantee": "postgres",
  268. "grantor": "postgres",
  269. "is_grantable": false,
  270. "privilege_type": "SELECT",
  271. },
  272. {
  273. "grantee": "postgres",
  274. "grantor": "postgres",
  275. "is_grantable": false,
  276. "privilege_type": "REFERENCES",
  277. },
  278. {
  279. "grantee": "postgres",
  280. "grantor": "postgres",
  281. "is_grantable": false,
  282. "privilege_type": "INSERT",
  283. },
  284. ],
  285. "relation_name": "t 1",
  286. "relation_schema": "public",
  287. }
  288. `
  289. )
  290. // Revoke all privileges
  291. const { sql: revokeSql } = pgMeta.columnPrivileges.revoke([
  292. {
  293. columnId: column_id,
  294. grantee: testRole,
  295. privilegeType: 'ALL',
  296. },
  297. ])
  298. await executeQuery(revokeSql)
  299. // Verify privileges were revoked
  300. privs = listZod.parse(await executeQuery(listSqlT1))
  301. expect(privs.length).toBe(1)
  302. expect(privs[0]).toMatchInlineSnapshot(
  303. { column_id: expect.stringMatching(/^\d+\.\d+$/) },
  304. `
  305. {
  306. "column_id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
  307. "column_name": "c 1",
  308. "privileges": [
  309. {
  310. "grantee": "postgres",
  311. "grantor": "postgres",
  312. "is_grantable": false,
  313. "privilege_type": "UPDATE",
  314. },
  315. {
  316. "grantee": "postgres",
  317. "grantor": "postgres",
  318. "is_grantable": false,
  319. "privilege_type": "SELECT",
  320. },
  321. {
  322. "grantee": "postgres",
  323. "grantor": "postgres",
  324. "is_grantable": false,
  325. "privilege_type": "REFERENCES",
  326. },
  327. {
  328. "grantee": "postgres",
  329. "grantor": "postgres",
  330. "is_grantable": false,
  331. "privilege_type": "INSERT",
  332. },
  333. ],
  334. "relation_name": "t 1",
  335. "relation_schema": "public",
  336. }
  337. `
  338. )
  339. }
  340. )