-
-
Notifications
You must be signed in to change notification settings - Fork 138
/
Copy pathPostgresMetaColumnPrivileges.ts
141 lines (134 loc) · 3.5 KB
/
PostgresMetaColumnPrivileges.ts
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
import { ident, literal } from 'pg-format'
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
import { filterByList } from './helpers.js'
import columnPrivilegesSql from './sql/column_privileges.sql'
import {
PostgresMetaResult,
PostgresColumnPrivileges,
PostgresColumnPrivilegesGrant,
PostgresColumnPrivilegesRevoke,
} from './types.js'
export default class PostgresMetaColumnPrivileges {
query: (sql: string) => Promise<PostgresMetaResult<any>>
constructor(query: (sql: string) => Promise<PostgresMetaResult<any>>) {
this.query = query
}
async list({
includeSystemSchemas = false,
includedSchemas,
excludedSchemas,
limit,
offset,
}: {
includeSystemSchemas?: boolean
includedSchemas?: string[]
excludedSchemas?: string[]
limit?: number
offset?: number
} = {}): Promise<PostgresMetaResult<PostgresColumnPrivileges[]>> {
let sql = `
with column_privileges as (${columnPrivilegesSql})
select *
from column_privileges
`
const filter = filterByList(
includedSchemas,
excludedSchemas,
!includeSystemSchemas ? DEFAULT_SYSTEM_SCHEMAS : undefined
)
if (filter) {
sql += ` where relation_schema ${filter}`
}
if (limit) {
sql += ` limit ${limit}`
}
if (offset) {
sql += ` offset ${offset}`
}
return await this.query(sql)
}
async grant(
grants: PostgresColumnPrivilegesGrant[]
): Promise<PostgresMetaResult<PostgresColumnPrivileges[]>> {
let sql = `
do $$
declare
col record;
begin
${grants
.map(({ privilege_type, column_id, grantee, is_grantable }) => {
const [relationId, columnNumber] = column_id.split('.')
return `
select *
from pg_attribute a
where a.attrelid = ${literal(relationId)}
and a.attnum = ${literal(columnNumber)}
into col;
execute format(
'grant ${privilege_type} (%I) on %s to ${
grantee.toLowerCase() === 'public' ? 'public' : ident(grantee)
} ${is_grantable ? 'with grant option' : ''}',
col.attname,
col.attrelid::regclass
);`
})
.join('\n')}
end $$;
`
const { data, error } = await this.query(sql)
if (error) {
return { data, error }
}
// Return the updated column privileges for modified columns.
const columnIds = [...new Set(grants.map(({ column_id }) => column_id))]
sql = `
with column_privileges as (${columnPrivilegesSql})
select *
from column_privileges
where column_id in (${columnIds.map(literal).join(',')})
`
return await this.query(sql)
}
async revoke(
revokes: PostgresColumnPrivilegesRevoke[]
): Promise<PostgresMetaResult<PostgresColumnPrivileges[]>> {
let sql = `
do $$
declare
col record;
begin
${revokes
.map(({ privilege_type, column_id, grantee }) => {
const [relationId, columnNumber] = column_id.split('.')
return `
select *
from pg_attribute a
where a.attrelid = ${literal(relationId)}
and a.attnum = ${literal(columnNumber)}
into col;
execute format(
'revoke ${privilege_type} (%I) on %s from ${
grantee.toLowerCase() === 'public' ? 'public' : ident(grantee)
}',
col.attname,
col.attrelid::regclass
);`
})
.join('\n')}
end $$;
`
const { data, error } = await this.query(sql)
if (error) {
return { data, error }
}
// Return the updated column privileges for modified columns.
const columnIds = [...new Set(revokes.map(({ column_id }) => column_id))]
sql = `
with column_privileges as (${columnPrivilegesSql})
select *
from column_privileges
where column_id in (${columnIds.map(literal).join(',')})
`
return await this.query(sql)
}
}