From 55b988dbe7c8dd5804931940fd9a9719c37a28bf Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Sun, 29 Jan 2023 14:19:46 +0100 Subject: [PATCH 01/19] feature: add column permission to lib --- src/lib/PostgresMeta.ts | 3 + src/lib/PostgresMetaColumnPermissions.ts | 117 +++++++++++++++++++++++ src/lib/index.ts | 1 + src/lib/sql/column_permissions.sql | 82 ++++++++++++++++ src/lib/sql/index.ts | 4 + src/lib/types.ts | 13 +++ 6 files changed, 220 insertions(+) create mode 100644 src/lib/PostgresMetaColumnPermissions.ts create mode 100644 src/lib/sql/column_permissions.sql diff --git a/src/lib/PostgresMeta.ts b/src/lib/PostgresMeta.ts index 04d6634f..252bae4f 100644 --- a/src/lib/PostgresMeta.ts +++ b/src/lib/PostgresMeta.ts @@ -1,5 +1,6 @@ import { PoolConfig } from 'pg' import * as Parser from './Parser.js' +import PostgresMetaColumnPermissions from './PostgresMetaColumnPermissions.js' import PostgresMetaColumns from './PostgresMetaColumns.js' import PostgresMetaConfig from './PostgresMetaConfig.js' import PostgresMetaExtensions from './PostgresMetaExtensions.js' @@ -20,6 +21,7 @@ import { PostgresMetaResult } from './types.js' export default class PostgresMeta { query: (sql: string) => Promise> end: () => Promise + columnPermissions: PostgresMetaColumnPermissions columns: PostgresMetaColumns config: PostgresMetaConfig extensions: PostgresMetaExtensions @@ -44,6 +46,7 @@ export default class PostgresMeta { this.query = query this.end = end this.columns = new PostgresMetaColumns(this.query) + this.columnPermissions = new PostgresMetaColumnPermissions(this.query) this.config = new PostgresMetaConfig(this.query) this.extensions = new PostgresMetaExtensions(this.query) this.foreignTables = new PostgresMetaForeignTables(this.query) diff --git a/src/lib/PostgresMetaColumnPermissions.ts b/src/lib/PostgresMetaColumnPermissions.ts new file mode 100644 index 00000000..669e38b6 --- /dev/null +++ b/src/lib/PostgresMetaColumnPermissions.ts @@ -0,0 +1,117 @@ +import { ident, literal } from 'pg-format' +import { columnPermissionsSql } from './sql/index.js' +import { PostgresMetaResult, PostgresColumnPermission } from './types.js' + +export default class PostgresMetaColumns { + query: (sql: string) => Promise> + + constructor(query: (sql: string) => Promise>) { + this.query = query + } + + async list({ + table_schema, + table_name, + column_name, + privilege, + include_system_schemas = false, + limit, + offset, + }: { + table_schema?: string + table_name?: string + column_name?: string + privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' + include_system_schemas?: boolean + limit?: number + offset?: number + } = {}): Promise> { + let sql = ` +WITH + column_permissions AS (${columnPermissionsSql}) +SELECT + * +FROM + column_permissions +WHERE + true` + if (table_schema) { + sql += ` AND table_schema = ${literal(table_schema)}` + } + if (table_name) { + sql += ` AND table_name = ${literal(table_name)}` + } + if (column_name) { + sql += ` AND column_name = ${literal(column_name)}` + } + if (privilege) { + sql += ` AND privilege = ${literal(privilege)}` + } + if (!include_system_schemas) { + sql += ` AND table_schema NOT LIKE 'pg_%'` + sql += ` AND table_schema != 'information_schema'` + } + if (limit) { + sql += ` LIMIT ${limit}` + } + if (offset) { + sql += ` OFFSET ${offset}` + } + console.log(sql) + return await this.query(sql) + } + + async grant( + column_name: string, + { + table_schema, + table_name, + privilege_type, + role, + }: { + table_schema: string + table_name: string + privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' + role: string + } + ): Promise> { + let sql = 'GRANT ' + sql += privilege_type ?? 'ALL PRIVILEGES' + sql += ` (${ident(column_name)}) on ${ident(table_schema)}.${ident(table_name)}` + sql += ` to ${ident(role)}` + { + const { error } = await this.query(sql) + if (error) { + return { data: null, error } + } + } + return { data: 'OK', error: null } + } + + async revoke( + column_name: string, + { + table_schema, + table_name, + privilege_type, + role, + }: { + table_schema: string + table_name: string + privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' + role: string + } + ): Promise> { + let sql = 'REVOKE ' + sql += privilege_type ?? 'ALL PRIVILEGES' + sql += ` (${ident(column_name)}) on ${ident(table_schema)}.${ident(table_name)}` + sql += ` to ${ident(role)}` + { + const { error } = await this.query(sql) + if (error) { + return { data: null, error } + } + } + return { data: 'OK', error: null } + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts index 1fe3fd5a..5987ec2f 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -3,6 +3,7 @@ export { PostgresMetaOk, PostgresMetaErr, PostgresMetaResult, + PostgresColumnPermission, PostgresColumn, PostgresConfig, PostgresExtension, diff --git a/src/lib/sql/column_permissions.sql b/src/lib/sql/column_permissions.sql new file mode 100644 index 00000000..c941da9d --- /dev/null +++ b/src/lib/sql/column_permissions.sql @@ -0,0 +1,82 @@ +-- Adapted from information_schema.column_privileges view +-- Read all roles, not only those granted to login role of postgres-meta +-- Deleted database column +-- Deleted is_grantable column + +SELECT + nc.nspname as table_schema, + c.relname as table_name, + pr_a.attname as column_name, + pr_a.prtype as privilege, + u_grantor.rolname as grantor, + grantee.rolname as grantee +FROM + ( + SELECT + a.attrelid, + a.attname, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).grantor AS grantor, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).grantee AS grantee, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).privilege_type AS privilege_type + FROM + pg_attribute a + JOIN pg_class cc ON a.attrelid = cc.oid + WHERE + a.attnum > 0 + AND NOT a.attisdropped + ) pr_a( + attrelid, attname, grantor, grantee, + prtype + ), + pg_class c, + pg_namespace nc, + pg_authid u_grantor, + ( + SELECT + pg_authid.oid, + pg_authid.rolname + FROM + pg_authid + UNION ALL + SELECT + 0 :: oid AS oid, + 'PUBLIC' :: name + ) grantee(oid, rolname) +WHERE + pr_a.attrelid = c.oid + AND c.relnamespace = nc.oid + AND pr_a.grantee = grantee.oid + AND pr_a.grantor = u_grantor.oid + AND ( + pr_a.prtype = ANY ( + ARRAY[ 'INSERT' :: text, 'SELECT' :: text, + 'UPDATE' :: text, 'REFERENCES' :: text] + ) + ) + AND ( + c.relkind = ANY ( + ARRAY[ 'r' :: "char", 'v' :: "char", 'f' :: "char", + 'p' :: "char" ] + ) + ) diff --git a/src/lib/sql/index.ts b/src/lib/sql/index.ts index d941ccc9..5271ab7b 100644 --- a/src/lib/sql/index.ts +++ b/src/lib/sql/index.ts @@ -3,6 +3,10 @@ import { dirname, join } from 'path' import { fileURLToPath } from 'url' const __dirname = dirname(fileURLToPath(import.meta.url)) +export const columnPermissionsSql = await readFile( + join(__dirname, 'column_permissions.sql'), + 'utf-8' +) export const columnsSql = await readFile(join(__dirname, 'columns.sql'), 'utf-8') export const configSql = await readFile(join(__dirname, 'config.sql'), 'utf-8') export const extensionsSql = await readFile(join(__dirname, 'extensions.sql'), 'utf-8') diff --git a/src/lib/types.ts b/src/lib/types.ts index 599837fd..ac0c6b43 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -17,6 +17,19 @@ export interface PostgresMetaErr { export type PostgresMetaResult = PostgresMetaOk | PostgresMetaErr +export const postgresColumnPermissionSchema = Type.Object({ + role: Type.String(), + table_schema: Type.String(), + table_name: Type.String(), + column_name: Type.String(), + privilege_type: Type.Union([ + Type.Literal('SELECT'), + Type.Literal('INSERT'), + Type.Literal('UPDATE'), + ]), +}) +export type PostgresColumnPermission = Static + export const postgresColumnSchema = Type.Object({ table_id: Type.Integer(), schema: Type.String(), From 36da936ed218abb5fbd44ecf94f28273b26f441b Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Sun, 29 Jan 2023 14:38:44 +0100 Subject: [PATCH 02/19] feature: add routes for column permissions --- src/server/routes/column-permissions.ts | 150 ++++++++++++++++++++++++ src/server/routes/index.ts | 2 + 2 files changed, 152 insertions(+) create mode 100644 src/server/routes/column-permissions.ts diff --git a/src/server/routes/column-permissions.ts b/src/server/routes/column-permissions.ts new file mode 100644 index 00000000..21a90e57 --- /dev/null +++ b/src/server/routes/column-permissions.ts @@ -0,0 +1,150 @@ +import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' +import { Type } from '@sinclair/typebox' +import { PostgresMeta } from '../../lib/index.js' +import { postgresColumnPermissionSchema } from '../../lib/types.js' +import { DEFAULT_POOL_CONFIG } from '../constants.js' +import { extractRequestForLogging } from '../utils.js' + +const route: FastifyPluginAsyncTypebox = async (fastify) => { + fastify.get( + '/', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + querystring: Type.Object({ + table_schema: Type.Optional(Type.String()), + table_name: Type.Optional(Type.String()), + column_name: Type.Optional(Type.String()), + privilege_type: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + limit: Type.Optional(Type.Integer()), + offset: Type.Optional(Type.Integer()), + }), + response: { + 200: Type.Array(postgresColumnPermissionSchema), + 500: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + const table_schema = request.query.table_schema + const table_name = request.query.table_name + const column_name = request.query.column_name + const privilege_type = request.query.privilege_type + const limit = request.query.limit + const offset = request.query.offset + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.columnPermissions.list({ + table_schema, + table_name, + column_name, + privilege_type, + limit, + offset, + }) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(500) + return { error: error.message } + } + + return data + } + ) + + fastify.post( + '/:column_name', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + params: Type.Object({ + column_name: Type.String(), + }), + body: Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + role: Type.String(), + privilege_type: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + }), + response: { + 200: Type.Literal('OK'), + 404: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.columnPermissions.grant( + request.params.column_name, + request.body + ) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(400) + if (error.message.startsWith('Cannot find')) reply.code(404) + return { error: error.message } + } + + return data + } + ) + + fastify.delete( + '/:column_name', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + params: Type.Object({ + column_name: Type.String(), + }), + body: Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + role: Type.String(), + privilege_type: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + }), + response: { + 200: Type.Literal('OK'), + 404: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.columnPermissions.revoke( + request.params.column_name, + request.body + ) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(400) + if (error.message.startsWith('Cannot find')) reply.code(404) + return { error: error.message } + } + + return data + } + ) +} + +export default route diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index 0c01269e..69979f43 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -1,5 +1,6 @@ import CryptoJS from 'crypto-js' import { FastifyInstance } from 'fastify' +import ColumnPermissionsRoute from './column-permissions.js' import ColumnRoute from './columns.js' import ConfigRoute from './config.js' import ExtensionsRoute from './extensions.js' @@ -40,6 +41,7 @@ export default async (fastify: FastifyInstance) => { done() }) + fastify.register(ColumnPermissionsRoute, { prefix: '/column-permissions' }) fastify.register(ColumnRoute, { prefix: '/columns' }) fastify.register(ConfigRoute, { prefix: '/config' }) fastify.register(ExtensionsRoute, { prefix: '/extensions' }) From 139131809af34febc465eca0207b058b9d2e7ebe Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Tue, 31 Jan 2023 16:52:19 +0100 Subject: [PATCH 03/19] chore: create new file for input types --- src/lib/PostgresMetaColumnPermissions.ts | 11 ++--------- src/lib/inputs.ts | 23 +++++++++++++++++++++++ src/server/routes/column-permissions.ts | 12 ++---------- 3 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 src/lib/inputs.ts diff --git a/src/lib/PostgresMetaColumnPermissions.ts b/src/lib/PostgresMetaColumnPermissions.ts index 669e38b6..590f3c9d 100644 --- a/src/lib/PostgresMetaColumnPermissions.ts +++ b/src/lib/PostgresMetaColumnPermissions.ts @@ -1,4 +1,5 @@ import { ident, literal } from 'pg-format' +import { ColumnPermissionListSchema } from './inputs.js' import { columnPermissionsSql } from './sql/index.js' import { PostgresMetaResult, PostgresColumnPermission } from './types.js' @@ -17,15 +18,7 @@ export default class PostgresMetaColumns { include_system_schemas = false, limit, offset, - }: { - table_schema?: string - table_name?: string - column_name?: string - privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' - include_system_schemas?: boolean - limit?: number - offset?: number - } = {}): Promise> { + }: ColumnPermissionListSchema = {}): Promise> { let sql = ` WITH column_permissions AS (${columnPermissionsSql}) diff --git a/src/lib/inputs.ts b/src/lib/inputs.ts new file mode 100644 index 00000000..f8f99571 --- /dev/null +++ b/src/lib/inputs.ts @@ -0,0 +1,23 @@ +// This file includes all route input schemas + +import { Static, Type } from '@sinclair/typebox' + +const limit = Type.Optional(Type.Integer()) +const offset = Type.Optional(Type.Integer()) + +export const columnPermissionListSchema = Type.Object({ + table_schema: Type.Optional(Type.String()), + table_name: Type.Optional(Type.String()), + column_name: Type.Optional(Type.String()), + privilege_type: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + include_system_schemas: Type.Optional( + Type.Boolean({ + default: false, + }) + ), + limit, + offset, +}) +export type ColumnPermissionListSchema = Static diff --git a/src/server/routes/column-permissions.ts b/src/server/routes/column-permissions.ts index 21a90e57..3ca4e6e8 100644 --- a/src/server/routes/column-permissions.ts +++ b/src/server/routes/column-permissions.ts @@ -4,6 +4,7 @@ import { PostgresMeta } from '../../lib/index.js' import { postgresColumnPermissionSchema } from '../../lib/types.js' import { DEFAULT_POOL_CONFIG } from '../constants.js' import { extractRequestForLogging } from '../utils.js' +import { columnPermissionListSchema } from '../../lib/inputs.js' const route: FastifyPluginAsyncTypebox = async (fastify) => { fastify.get( @@ -11,16 +12,7 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => { { schema: { headers: Type.Object({ pg: Type.String() }), - querystring: Type.Object({ - table_schema: Type.Optional(Type.String()), - table_name: Type.Optional(Type.String()), - column_name: Type.Optional(Type.String()), - privilege_type: Type.Optional( - Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) - ), - limit: Type.Optional(Type.Integer()), - offset: Type.Optional(Type.Integer()), - }), + querystring: columnPermissionListSchema, response: { 200: Type.Array(postgresColumnPermissionSchema), 500: Type.Object({ From 4f0143854d1111d31a231fc748d16bb56edfd7e8 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 22:58:00 +0100 Subject: [PATCH 04/19] Add table_permissions sql --- src/lib/sql/index.ts | 1 + src/lib/sql/table_permissions.sql | 76 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/lib/sql/table_permissions.sql diff --git a/src/lib/sql/index.ts b/src/lib/sql/index.ts index 5271ab7b..e2b1c0c7 100644 --- a/src/lib/sql/index.ts +++ b/src/lib/sql/index.ts @@ -18,6 +18,7 @@ export const publicationsSql = await readFile(join(__dirname, 'publications.sql' export const relationshipsSql = await readFile(join(__dirname, 'relationships.sql'), 'utf-8') export const rolesSql = await readFile(join(__dirname, 'roles.sql'), 'utf-8') export const schemasSql = await readFile(join(__dirname, 'schemas.sql'), 'utf-8') +export const tablePermissionsSql = await readFile(join(__dirname, 'table_permissions.sql'), 'utf-8') export const tablesSql = await readFile(join(__dirname, 'tables.sql'), 'utf-8') export const triggersSql = await readFile(join(__dirname, 'triggers.sql'), 'utf-8') export const typesSql = await readFile(join(__dirname, 'types.sql'), 'utf-8') diff --git a/src/lib/sql/table_permissions.sql b/src/lib/sql/table_permissions.sql new file mode 100644 index 00000000..1efadec9 --- /dev/null +++ b/src/lib/sql/table_permissions.sql @@ -0,0 +1,76 @@ +-- Adapted from information_schema.column_privileges view +-- Read all roles, not only those granted to login role of postgres-meta +-- Deleted database column +-- Deleted is_grantable column + +SELECT + nc.nspname as table_schema, + pr_c.relname as table_name, + pr_c.prtype as privilege, + u_grantor.rolname as grantor, + grantee.rolname as grantee +FROM + ( + SELECT + pg_class.oid, + pg_class.relname, + pg_class.relnamespace, + pg_class.relowner, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).grantor AS grantor, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).grantee AS grantee, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).privilege_type AS privilege_type + FROM + pg_class + WHERE + pg_class.relkind = ANY ( + ARRAY[ 'r' :: "char", 'v' :: "char", 'f' :: "char", + 'p' :: "char" ] + ) + ) pr_c( + oid, relname, relnamespace, relowner, + grantor, grantee, prtype + ), + pg_namespace nc, + pg_authid u_grantor, + ( + SELECT + pg_authid.oid, + pg_authid.rolname + FROM + pg_authid + UNION ALL + SELECT + 0 :: oid AS oid, + 'PUBLIC' :: name + ) grantee(oid, rolname) +WHERE + pr_c.relnamespace = nc.oid + AND pr_c.grantee = grantee.oid + AND pr_c.grantor = u_grantor.oid + AND ( + pr_c.prtype = ANY ( + ARRAY[ 'INSERT' :: text, 'SELECT' :: text, + 'UPDATE' :: text, 'REFERENCES' :: text] + ) + ) \ No newline at end of file From 62f19a4a937a8f47746e9e282d6cdbb61863e1d9 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 22:58:22 +0100 Subject: [PATCH 05/19] Add permissions sql --- src/lib/sql/index.ts | 1 + src/lib/sql/permissions.sql | 152 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/lib/sql/permissions.sql diff --git a/src/lib/sql/index.ts b/src/lib/sql/index.ts index e2b1c0c7..df5c00e0 100644 --- a/src/lib/sql/index.ts +++ b/src/lib/sql/index.ts @@ -12,6 +12,7 @@ export const configSql = await readFile(join(__dirname, 'config.sql'), 'utf-8') export const extensionsSql = await readFile(join(__dirname, 'extensions.sql'), 'utf-8') export const foreignTablesSql = await readFile(join(__dirname, 'foreign_tables.sql'), 'utf-8') export const functionsSql = await readFile(join(__dirname, 'functions.sql'), 'utf-8') +export const permissionsSql = await readFile(join(__dirname, 'permissions.sql'), 'utf-8') export const policiesSql = await readFile(join(__dirname, 'policies.sql'), 'utf-8') export const primaryKeysSql = await readFile(join(__dirname, 'primary_keys.sql'), 'utf-8') export const publicationsSql = await readFile(join(__dirname, 'publications.sql'), 'utf-8') diff --git a/src/lib/sql/permissions.sql b/src/lib/sql/permissions.sql new file mode 100644 index 00000000..b0e1291c --- /dev/null +++ b/src/lib/sql/permissions.sql @@ -0,0 +1,152 @@ +-- Adapted from information_schema.column_privileges view +-- Read all roles, not only those granted to login role of postgres-meta +-- Deleted database column +-- Deleted is_grantable column + +SELECT + nc.nspname AS table_schema, + x.relname AS table_name, + x.attname AS column_name, + x.prtype AS privilege, + u_grantor.rolname AS grantor, + grantee.rolname AS grantee +FROM + ( + SELECT + pr_c.grantor, + pr_c.grantee, + a.attname, + pr_c.relname, + pr_c.relnamespace, + pr_c.prtype, + pr_c.grantable, + pr_c.relowner + FROM + ( + SELECT + pg_class.oid, + pg_class.relname, + pg_class.relnamespace, + pg_class.relowner, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).grantor AS grantor, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).grantee AS grantee, + ( + aclexplode( + COALESCE( + pg_class.relacl, + acldefault('r' :: "char", pg_class.relowner) + ) + ) + ).privilege_type AS privilege_type + FROM + pg_class + WHERE + pg_class.relkind = ANY ( + ARRAY[ 'r' :: "char", 'v' :: "char", 'f' :: "char", + 'p' :: "char" ] + ) + ) pr_c( + oid, relname, relnamespace, relowner, + grantor, grantee, prtype, grantable + ), + pg_attribute a + WHERE + a.attrelid = pr_c.oid + AND a.attnum > 0 + AND NOT a.attisdropped + UNION + SELECT + pr_a.grantor, + pr_a.grantee, + pr_a.attname, + c.relname, + c.relnamespace, + pr_a.prtype, + pr_a.grantable, + c.relowner + FROM + ( + SELECT + a.attrelid, + a.attname, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).grantor AS grantor, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).grantee AS grantee, + ( + aclexplode( + COALESCE( + a.attacl, + acldefault('c' :: "char", cc.relowner) + ) + ) + ).privilege_type AS privilege_type + FROM + pg_attribute a + JOIN pg_class cc ON a.attrelid = cc.oid + WHERE + a.attnum > 0 + AND NOT a.attisdropped + ) pr_a( + attrelid, attname, grantor, grantee, + prtype, grantable + ), + pg_class c + WHERE + pr_a.attrelid = c.oid + AND ( + c.relkind = ANY ( + ARRAY[ 'r' :: "char", 'v' :: "char", 'f' :: "char", + 'p' :: "char" ] + ) + ) + ) x, + pg_namespace nc, + pg_authid u_grantor, + ( + SELECT + pg_authid.oid, + pg_authid.rolname + FROM + pg_authid + UNION ALL + SELECT + 0 :: oid AS oid, + 'PUBLIC' :: name + ) grantee(oid, rolname) +WHERE + x.relnamespace = nc.oid + AND x.grantee = grantee.oid + AND x.grantor = u_grantor.oid + AND ( + x.prtype = ANY ( + ARRAY[ 'INSERT' :: text, 'SELECT' :: text, + 'UPDATE' :: text, 'REFERENCES' :: text] + ) + ) From 703b5df957758a367b3ef7fa91c5ca27e7b011f1 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 22:59:18 +0100 Subject: [PATCH 06/19] Add route for table-permission --- src/server/routes/table-permission.ts | 136 ++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/server/routes/table-permission.ts diff --git a/src/server/routes/table-permission.ts b/src/server/routes/table-permission.ts new file mode 100644 index 00000000..1edfcffa --- /dev/null +++ b/src/server/routes/table-permission.ts @@ -0,0 +1,136 @@ +import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' +import { Type } from '@sinclair/typebox' +import { PostgresMeta } from '../../lib/index.js' +import { postgresTablePermissionSchema } from '../../lib/types.js' +import { DEFAULT_POOL_CONFIG } from '../constants.js' +import { extractRequestForLogging } from '../utils.js' +import { tablePermissionListSchema } from '../../lib/inputs.js' + +const route: FastifyPluginAsyncTypebox = async (fastify) => { + fastify.get( + '/', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + querystring: tablePermissionListSchema, + response: { + 200: Type.Array(postgresTablePermissionSchema), + 500: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + const { table_schema, table_name, privilege, limit, offset } = request.query + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.tablePermissions.list({ + table_schema, + table_name, + privilege, + limit, + offset, + }) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(500) + return { error: error.message } + } + + return data + } + ) + + fastify.post( + '/:table_name', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + params: Type.Object({ + table_name: Type.String(), + }), + body: Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + role: Type.String(), + privilege: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + }), + response: { + 200: Type.Literal('OK'), + 404: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.tablePermissions.grant( + request.params.table_name, + request.body + ) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(400) + if (error.message.startsWith('Cannot find')) reply.code(404) + return { error: error.message } + } + + return data + } + ) + + fastify.delete( + '/:table_name', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + params: Type.Object({ + table_name: Type.String(), + }), + body: Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + role: Type.String(), + privilege: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + }), + response: { + 200: Type.Literal('OK'), + 404: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.tablePermissions.revoke( + request.params.table_name, + request.body + ) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(400) + if (error.message.startsWith('Cannot find')) reply.code(404) + return { error: error.message } + } + + return data + } + ) +} + +export default route From 1872bf92a27baf08582edb60309cd7a752d1e856 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 22:59:31 +0100 Subject: [PATCH 07/19] Add route for permission --- src/server/routes/permission.ts | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/server/routes/permission.ts diff --git a/src/server/routes/permission.ts b/src/server/routes/permission.ts new file mode 100644 index 00000000..16177687 --- /dev/null +++ b/src/server/routes/permission.ts @@ -0,0 +1,49 @@ +import { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' +import { Type } from '@sinclair/typebox' +import { PostgresMeta } from '../../lib/index.js' +import { postgresPermissionSchema } from '../../lib/types.js' +import { DEFAULT_POOL_CONFIG } from '../constants.js' +import { extractRequestForLogging } from '../utils.js' +import { permissionListSchema } from '../../lib/inputs.js' + +const route: FastifyPluginAsyncTypebox = async (fastify) => { + fastify.get( + '/', + { + schema: { + headers: Type.Object({ pg: Type.String() }), + querystring: permissionListSchema, + response: { + 200: Type.Array(postgresPermissionSchema), + 500: Type.Object({ + error: Type.String(), + }), + }, + }, + }, + async (request, reply) => { + const connectionString = request.headers.pg + const { table_schema, table_name, column_name, privilege, limit, offset } = request.query + + const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) + const { data, error } = await pgMeta.permissions.list({ + table_schema, + table_name, + column_name, + privilege, + limit, + offset, + }) + await pgMeta.end() + if (error) { + request.log.error({ error, request: extractRequestForLogging(request) }) + reply.code(500) + return { error: error.message } + } + + return data + } + ) +} + +export default route From 61bfadad66ebb48866cf49f0edd550e87f338c3e Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 22:59:40 +0100 Subject: [PATCH 08/19] Update column-permissions.ts --- src/server/routes/column-permissions.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/server/routes/column-permissions.ts b/src/server/routes/column-permissions.ts index 3ca4e6e8..987fc565 100644 --- a/src/server/routes/column-permissions.ts +++ b/src/server/routes/column-permissions.ts @@ -23,19 +23,14 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => { }, async (request, reply) => { const connectionString = request.headers.pg - const table_schema = request.query.table_schema - const table_name = request.query.table_name - const column_name = request.query.column_name - const privilege_type = request.query.privilege_type - const limit = request.query.limit - const offset = request.query.offset + const { table_schema, table_name, column_name, privilege, limit, offset } = request.query const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString }) const { data, error } = await pgMeta.columnPermissions.list({ table_schema, table_name, column_name, - privilege_type, + privilege, limit, offset, }) @@ -62,7 +57,7 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => { table_schema: Type.String(), table_name: Type.String(), role: Type.String(), - privilege_type: Type.Optional( + privilege: Type.Optional( Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) ), }), @@ -106,7 +101,7 @@ const route: FastifyPluginAsyncTypebox = async (fastify) => { table_schema: Type.String(), table_name: Type.String(), role: Type.String(), - privilege_type: Type.Optional( + privilege: Type.Optional( Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) ), }), From 6a2756f76eba53eb9d1ce521f7b19286a1c388a3 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:00:21 +0100 Subject: [PATCH 09/19] Add TablePermission to Meta --- src/lib/PostgresMeta.ts | 3 + src/lib/PostgresMetaTablePermissions.ts | 102 ++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/lib/PostgresMetaTablePermissions.ts diff --git a/src/lib/PostgresMeta.ts b/src/lib/PostgresMeta.ts index 252bae4f..bf7c1779 100644 --- a/src/lib/PostgresMeta.ts +++ b/src/lib/PostgresMeta.ts @@ -10,6 +10,7 @@ import PostgresMetaPolicies from './PostgresMetaPolicies.js' import PostgresMetaPublications from './PostgresMetaPublications.js' import PostgresMetaRoles from './PostgresMetaRoles.js' import PostgresMetaSchemas from './PostgresMetaSchemas.js' +import PostgresMetaTablePermissions from './PostgresMetaTablePermissions.js' import PostgresMetaTables from './PostgresMetaTables.js' import PostgresMetaTriggers from './PostgresMetaTriggers.js' import PostgresMetaTypes from './PostgresMetaTypes.js' @@ -31,6 +32,7 @@ export default class PostgresMeta { publications: PostgresMetaPublications roles: PostgresMetaRoles schemas: PostgresMetaSchemas + tablePermissions: PostgresMetaTablePermissions tables: PostgresMetaTables triggers: PostgresMetaTriggers types: PostgresMetaTypes @@ -55,6 +57,7 @@ export default class PostgresMeta { this.publications = new PostgresMetaPublications(this.query) this.roles = new PostgresMetaRoles(this.query) this.schemas = new PostgresMetaSchemas(this.query) + this.tablePermissions = new PostgresMetaTablePermissions(this.query) this.tables = new PostgresMetaTables(this.query) this.triggers = new PostgresMetaTriggers(this.query) this.types = new PostgresMetaTypes(this.query) diff --git a/src/lib/PostgresMetaTablePermissions.ts b/src/lib/PostgresMetaTablePermissions.ts new file mode 100644 index 00000000..9b4341a3 --- /dev/null +++ b/src/lib/PostgresMetaTablePermissions.ts @@ -0,0 +1,102 @@ +import { ident, literal } from 'pg-format' +import { tablePermissionsSql } from './sql/index.js' +import type { TablePermissionListSchema } from './inputs.js' +import type { PostgresMetaResult, PostgresTablePermission } from './types.js' + +export default class PostgresMetaColumns { + query: (sql: string) => Promise> + + constructor(query: (sql: string) => Promise>) { + this.query = query + } + + async list({ + table_schema, + table_name, + privilege, + include_system_schemas = false, + limit, + offset, + }: TablePermissionListSchema = {}): Promise> { + let sql = ` +WITH + table_permissions AS (${tablePermissionsSql}) +SELECT + * +FROM + table_permissions +WHERE + true` + if (table_schema) { + sql += ` AND table_schema = ${literal(table_schema)}` + } + if (table_name) { + sql += ` AND table_name = ${literal(table_name)}` + } + if (privilege) { + sql += ` AND privilege = ${literal(privilege)}` + } + if (!include_system_schemas) { + sql += ` AND table_schema NOT LIKE 'pg_%'` + sql += ` AND table_schema != 'information_schema'` + } + if (limit) { + sql += ` LIMIT ${limit}` + } + if (offset) { + sql += ` OFFSET ${offset}` + } + console.log(sql) + return await this.query(sql) + } + + async grant( + table_name: string, + { + table_schema, + privilege_type, + role, + }: { + table_schema: string + privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' + role: string + } + ): Promise> { + let sql = 'GRANT ' + sql += privilege_type ?? 'ALL PRIVILEGES' + sql += ` on ${ident(table_schema)}.${ident(table_name)}` + sql += ` to ${ident(role)}` + { + const { error } = await this.query(sql) + if (error) { + return { data: null, error } + } + } + return { data: 'OK', error: null } + } + + async revoke( + table_name: string, + { + table_schema, + privilege_type, + role, + }: { + table_schema: string + privilege_type?: 'SELECT' | 'INSERT' | 'UPDATE' + role: string + } + ): Promise> { + let sql = 'REVOKE ' + sql += privilege_type ?? 'ALL PRIVILEGES' + sql += ` on ${ident(table_schema)}.${ident(table_name)}` + sql += ` to ${ident(role)}` + { + const { error } = await this.query(sql) + if (error) { + return { data: null, error } + } + } + return { data: 'OK', error: null } + } +} From 9a7a5db29b6c473326d1688459ed947c0ec2c65a Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:00:38 +0100 Subject: [PATCH 10/19] Add Permission to Meta --- src/lib/PostgresMeta.ts | 3 ++ src/lib/PostgresMetaPermissions.ts | 56 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/lib/PostgresMetaPermissions.ts diff --git a/src/lib/PostgresMeta.ts b/src/lib/PostgresMeta.ts index bf7c1779..06844ba2 100644 --- a/src/lib/PostgresMeta.ts +++ b/src/lib/PostgresMeta.ts @@ -6,6 +6,7 @@ import PostgresMetaConfig from './PostgresMetaConfig.js' import PostgresMetaExtensions from './PostgresMetaExtensions.js' import PostgresMetaForeignTables from './PostgresMetaForeignTables.js' import PostgresMetaFunctions from './PostgresMetaFunctions.js' +import PostgresMetaPermissions from './PostgresMetaPermissions.js' import PostgresMetaPolicies from './PostgresMetaPolicies.js' import PostgresMetaPublications from './PostgresMetaPublications.js' import PostgresMetaRoles from './PostgresMetaRoles.js' @@ -28,6 +29,7 @@ export default class PostgresMeta { extensions: PostgresMetaExtensions foreignTables: PostgresMetaForeignTables functions: PostgresMetaFunctions + permissions: PostgresMetaPermissions policies: PostgresMetaPolicies publications: PostgresMetaPublications roles: PostgresMetaRoles @@ -53,6 +55,7 @@ export default class PostgresMeta { this.extensions = new PostgresMetaExtensions(this.query) this.foreignTables = new PostgresMetaForeignTables(this.query) this.functions = new PostgresMetaFunctions(this.query) + this.permissions = new PostgresMetaPermissions(this.query) this.policies = new PostgresMetaPolicies(this.query) this.publications = new PostgresMetaPublications(this.query) this.roles = new PostgresMetaRoles(this.query) diff --git a/src/lib/PostgresMetaPermissions.ts b/src/lib/PostgresMetaPermissions.ts new file mode 100644 index 00000000..3109886b --- /dev/null +++ b/src/lib/PostgresMetaPermissions.ts @@ -0,0 +1,56 @@ +import { ident, literal } from 'pg-format' +import { PermissionListSchema } from './inputs.js' +import { permissionsSql } from './sql/index.js' +import { PostgresMetaResult, PostgresPermission } from './types.js' + +export default class PostgresMetaColumns { + query: (sql: string) => Promise> + + constructor(query: (sql: string) => Promise>) { + this.query = query + } + + async list({ + table_schema, + table_name, + column_name, + privilege, + include_system_schemas = false, + limit, + offset, + }: PermissionListSchema = {}): Promise> { + let sql = ` +WITH + permissions AS (${permissionsSql}) +SELECT + * +FROM + permissions +WHERE + true` + if (table_schema) { + sql += ` AND table_schema = ${literal(table_schema)}` + } + if (table_name) { + sql += ` AND table_name = ${literal(table_name)}` + } + if (column_name) { + sql += ` AND column_name = ${literal(column_name)}` + } + if (privilege) { + sql += ` AND privilege = ${literal(privilege)}` + } + if (!include_system_schemas) { + sql += ` AND table_schema NOT LIKE 'pg_%'` + sql += ` AND table_schema != 'information_schema'` + } + if (limit) { + sql += ` LIMIT ${limit}` + } + if (offset) { + sql += ` OFFSET ${offset}` + } + console.log(sql) + return await this.query(sql) + } +} From 8d4be9d61e6b385f86b63f433693687a94c475d7 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:00:57 +0100 Subject: [PATCH 11/19] Update index.ts --- src/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/index.ts b/src/lib/index.ts index 5987ec2f..b50fab48 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -17,6 +17,7 @@ export { PostgresSchema, PostgresSchemaCreate, PostgresSchemaUpdate, + PostgresTablePermission, PostgresTable, PostgresTrigger, PostgresType, From ef0a1e8bf7ad455b8e57c6a275d6662ebf1207d9 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:01:02 +0100 Subject: [PATCH 12/19] Update index.ts --- src/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/index.ts b/src/lib/index.ts index b50fab48..241a540c 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -9,6 +9,7 @@ export { PostgresExtension, PostgresFunction, PostgresFunctionCreate, + PostgresPermission, PostgresPolicy, PostgresPrimaryKey, PostgresPublication, From 8c54331cb6043c82e578f94d5f40f0e197778c80 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:01:17 +0100 Subject: [PATCH 13/19] Update types.ts --- src/lib/types.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index ac0c6b43..80534555 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -18,15 +18,16 @@ export interface PostgresMetaErr { export type PostgresMetaResult = PostgresMetaOk | PostgresMetaErr export const postgresColumnPermissionSchema = Type.Object({ - role: Type.String(), table_schema: Type.String(), table_name: Type.String(), column_name: Type.String(), - privilege_type: Type.Union([ + privilege: Type.Union([ Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE'), ]), + grantor: Type.String(), + grantee: Type.String(), }) export type PostgresColumnPermission = Static From 35b99d68ac5640568deeea005133d45263ee667a Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:01:33 +0100 Subject: [PATCH 14/19] Add permissionSchema type --- src/lib/types.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/types.ts b/src/lib/types.ts index 80534555..a202eb49 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -149,6 +149,20 @@ export const postgresFunctionCreateFunction = Type.Object({ }) export type PostgresFunctionCreate = Static +export const postgresPermissionSchema = Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + column_name: Type.String(), + privilege: Type.Union([ + Type.Literal('SELECT'), + Type.Literal('INSERT'), + Type.Literal('UPDATE'), + ]), + grantor: Type.String(), + grantee: Type.String(), +}) +export type PostgresPermission = Static + export const postgresPolicySchema = Type.Object({ id: Type.Integer(), schema: Type.String(), From 5df4e0ae46b428cb6084df0c58bf24ebb9fc5a67 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:01:45 +0100 Subject: [PATCH 15/19] Add tablePermissionSchema type --- src/lib/types.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/types.ts b/src/lib/types.ts index a202eb49..10774bc3 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -303,6 +303,20 @@ export const postgresSchemaUpdateSchema = Type.Object({ }) export type PostgresSchemaUpdate = Static +export const postgresTablePermissionSchema = Type.Object({ + table_schema: Type.String(), + table_name: Type.String(), + column_name: Type.String(), + privilege_type: Type.Union([ + Type.Literal('SELECT'), + Type.Literal('INSERT'), + Type.Literal('UPDATE'), + ]), + grantor: Type.String(), + grantee: Type.String(), +}) +export type PostgresTablePermission = Static + export const postgresTableSchema = Type.Object({ id: Type.Integer(), schema: Type.String(), From e6bc01071061b6a3066848252cab1dc0e58c818c Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:01:52 +0100 Subject: [PATCH 16/19] Update inputs.ts --- src/lib/inputs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/inputs.ts b/src/lib/inputs.ts index f8f99571..6c30c121 100644 --- a/src/lib/inputs.ts +++ b/src/lib/inputs.ts @@ -9,7 +9,7 @@ export const columnPermissionListSchema = Type.Object({ table_schema: Type.Optional(Type.String()), table_name: Type.Optional(Type.String()), column_name: Type.Optional(Type.String()), - privilege_type: Type.Optional( + privilege: Type.Optional( Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) ), include_system_schemas: Type.Optional( From 4edb3e59a43b4753be6ec0a11644a7596df4c6fe Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:02:12 +0100 Subject: [PATCH 17/19] Add permission input type --- src/lib/inputs.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib/inputs.ts b/src/lib/inputs.ts index 6c30c121..39b5df21 100644 --- a/src/lib/inputs.ts +++ b/src/lib/inputs.ts @@ -21,3 +21,21 @@ export const columnPermissionListSchema = Type.Object({ offset, }) export type ColumnPermissionListSchema = Static + +export const permissionListSchema = Type.Object({ + table_schema: Type.Optional(Type.String()), + table_name: Type.Optional(Type.String()), + column_name: Type.Optional(Type.String()), + privilege: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + include_system_schemas: Type.Optional( + Type.Boolean({ + default: false, + }) + ), + limit, + offset, +}) +export type PermissionListSchema = Static + From ba4d7e0213af261dc3d4d3b6bc577798bd6bc633 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:02:28 +0100 Subject: [PATCH 18/19] Add tablePermission input schema --- src/lib/inputs.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/inputs.ts b/src/lib/inputs.ts index 39b5df21..2b54c0a9 100644 --- a/src/lib/inputs.ts +++ b/src/lib/inputs.ts @@ -39,3 +39,18 @@ export const permissionListSchema = Type.Object({ }) export type PermissionListSchema = Static +export const tablePermissionListSchema = Type.Object({ + table_schema: Type.Optional(Type.String()), + table_name: Type.Optional(Type.String()), + privilege: Type.Optional( + Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]) + ), + include_system_schemas: Type.Optional( + Type.Boolean({ + default: false, + }) + ), + limit, + offset, +}) +export type TablePermissionListSchema = Static From 269d97430287e9f34de58b8399dd89234cc300d3 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Thu, 9 Feb 2023 23:05:54 +0100 Subject: [PATCH 19/19] Update types.ts --- src/lib/types.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index 10774bc3..7886bbf1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -21,11 +21,7 @@ export const postgresColumnPermissionSchema = Type.Object({ table_schema: Type.String(), table_name: Type.String(), column_name: Type.String(), - privilege: Type.Union([ - Type.Literal('SELECT'), - Type.Literal('INSERT'), - Type.Literal('UPDATE'), - ]), + privilege: Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]), grantor: Type.String(), grantee: Type.String(), }) @@ -153,11 +149,7 @@ export const postgresPermissionSchema = Type.Object({ table_schema: Type.String(), table_name: Type.String(), column_name: Type.String(), - privilege: Type.Union([ - Type.Literal('SELECT'), - Type.Literal('INSERT'), - Type.Literal('UPDATE'), - ]), + privilege: Type.Union([Type.Literal('SELECT'), Type.Literal('INSERT'), Type.Literal('UPDATE')]), grantor: Type.String(), grantee: Type.String(), })