@fabrika/platform-node 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabrika/platform-node",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,6 +37,6 @@
37
37
  "test": "bun test"
38
38
  },
39
39
  "dependencies": {
40
- "@fabrika/platform": "^0.4.5"
40
+ "@fabrika/platform": "^0.4.6"
41
41
  }
42
42
  }
@@ -18,12 +18,26 @@ export interface TestProvisioner {
18
18
  */
19
19
  export const DATABASE_GRANT_LOCK_KEY: number = createHash('sha256').update('fabrika-db:database-grants', 'utf8').digest().readUIntBE(0, 6) + 1
20
20
 
21
- /** Grants (or revokes) CREATE on `database` to `role`, serialized with every other run that does so on the same server. */
21
+ /**
22
+ * Grants (or revokes) CREATE on `database` to `role`, serialized with every other run that does so on the same server.
23
+ * Verified afterwards: a grantor that neither owns the database, is a superuser nor holds CREATE WITH GRANT OPTION only
24
+ * gets a WARNING, and the statement changes nothing.
25
+ */
22
26
  export async function changeCreateOnDatabase(admin: SqlDatabase, change: 'grant' | 'revoke', role: string, database: string): Promise<void> {
23
27
  const statement = change === 'grant'
24
28
  ? `GRANT CREATE ON DATABASE ${quotedIdentifier(database)} TO ${quotedIdentifier(role)}`
25
29
  : `REVOKE CREATE ON DATABASE ${quotedIdentifier(database)} FROM ${quotedIdentifier(role)}`
26
30
  await admin.batch([admin.prepare(`SELECT pg_catalog.pg_advisory_xact_lock(${DATABASE_GRANT_LOCK_KEY})`), admin.prepare(statement)])
31
+ const held = await admin.prepare("SELECT pg_catalog.has_database_privilege(?, ?, 'CREATE') AS creates").bind(role, database).first<{
32
+ creates: boolean
33
+ }>()
34
+ if ((held?.creates ?? false) !== (change === 'grant')) {
35
+ throw new PostgresMigrationError(
36
+ `the provisioning credential could not ${change} CREATE on the database ${database} ${
37
+ change === 'grant' ? 'to' : 'from'
38
+ } ${role}: it must own the database, be a superuser, or hold CREATE on it WITH GRANT OPTION`,
39
+ )
40
+ }
27
41
  }
28
42
 
29
43
  /** Before Postgres 16, CREATEROLE may grant membership in any role that is not a superuser, `pg_execute_server_program` included. */
@@ -58,7 +72,12 @@ export async function createTestProvisioner(provisioningUrl: string): Promise<Te
58
72
  )
59
73
  }
60
74
  await admin.prepare(`CREATE ROLE ${name} LOGIN CREATEROLE NOINHERIT PASSWORD '${scramVerifier(password)}'`).run()
61
- await changeCreateOnDatabase(admin, 'grant', name, self.database)
75
+ try {
76
+ await changeCreateOnDatabase(admin, 'grant', name, self.database)
77
+ } catch (error) {
78
+ await admin.prepare(`DROP ROLE ${name}`).run()
79
+ throw error
80
+ }
62
81
  const database = self.database
63
82
  url.username = name
64
83
  url.password = password