From 25b36f8c739e7c58175c44c3e3297708035e16a3 Mon Sep 17 00:00:00 2001 From: Roman Vaivod Date: Mon, 10 Jan 2022 12:03:06 +0100 Subject: [PATCH] Add revoke tests --- src/index.ts | 31 +++++++++++++++++++ src/operations/grants.ts | 26 ++++++++++++++-- src/operations/grantsTypes.ts | 21 +++++++------ test/migrations/086_grant_test.js | 6 ++-- .../087_revoke_tables_schemas_roles.js | 17 ++++++++++ test/migrations/088_revoke_test.js | 22 +++++++++++++ 6 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 test/migrations/087_revoke_tables_schemas_roles.js create mode 100644 test/migrations/088_revoke_test.js diff --git a/src/index.ts b/src/index.ts index 34053e4a0..4fe75e96a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -118,6 +118,23 @@ import { RefreshMaterializedViewOptions, } from './operations/viewsMaterializedTypes' +import { + TablePrivilege, + SchemaPrivilege, + GrantRoles, + GrantRolesOptions, + GrantOnTables, + GrantOnTablesProps, + GrantOnSchemas, + GrantOnSchemasProps, + RevokeRoles, + RevokeRolesOptions, + RevokeOnTables, + RevokeOnTablesProps, + RevokeOnSchemas, + RevokeOnSchemasProps, +} from './operations/grants' + export { PgLiteral, Migration, @@ -235,6 +252,20 @@ export { CreateMaterializedViewOptions, AlterMaterializedViewOptions, RefreshMaterializedViewOptions, + TablePrivilege, + SchemaPrivilege, + GrantRoles, + GrantRolesOptions, + GrantOnTables, + GrantOnTablesProps, + GrantOnSchemas, + GrantOnSchemasProps, + RevokeRoles, + RevokeRolesOptions, + RevokeOnTables, + RevokeOnTablesProps, + RevokeOnSchemas, + RevokeOnSchemasProps, } export default runner diff --git a/src/operations/grants.ts b/src/operations/grants.ts index 0d4abb296..f3fe72fce 100644 --- a/src/operations/grants.ts +++ b/src/operations/grants.ts @@ -1,16 +1,38 @@ import { MigrationOptions } from '../types' import { + TablePrivilege, + SchemaPrivilege, GrantRoles, + GrantRolesOptions, GrantOnTables, - GrantOnSchemas, GrantOnTablesProps, GrantOnAllTablesProps, + GrantOnSchemas, + GrantOnSchemasProps, RevokeRoles, + RevokeRolesOptions, RevokeOnTables, + RevokeOnTablesProps, RevokeOnSchemas, + RevokeOnSchemasProps, } from './grantsTypes' -export { GrantRoles, RevokeRoles, GrantOnTables, RevokeOnTables, GrantOnSchemas, RevokeOnSchemas } +export { + TablePrivilege, + SchemaPrivilege, + GrantRoles, + GrantRolesOptions, + GrantOnTables, + GrantOnTablesProps, + GrantOnSchemas, + GrantOnSchemasProps, + RevokeRoles, + RevokeRolesOptions, + RevokeOnTables, + RevokeOnTablesProps, + RevokeOnSchemas, + RevokeOnSchemasProps, +} const isArray = (item: T | T[]): item is T[] => { return typeof item !== 'string' && Boolean((item as Array).length !== undefined) diff --git a/src/operations/grantsTypes.ts b/src/operations/grantsTypes.ts index c65a520c0..1faf45157 100644 --- a/src/operations/grantsTypes.ts +++ b/src/operations/grantsTypes.ts @@ -20,22 +20,22 @@ interface WithGrantOption { readonly withGrantOption?: boolean } -type DropRolesOptions = OnlyAdminOption & RestrictOption - +export type RevokeRolesOptions = OnlyAdminOption & RestrictOption +export type GrantRolesOptions = WithAdminOption & RevokeRolesOptions type GrantRolesFn = ( rolesFrom: Name | Name[], rolesTo: Name | Name[], - grantRolesOptions?: WithAdminOption & DropRolesOptions, + grantRolesOptions?: GrantRolesOptions, ) => string | string[] export type GrantRoles = GrantRolesFn & { reverse: GrantRolesFn } export type RevokeRoles = ( roles: Name | Name[], rolesFrom: Name | Name[], - dropRolesOptions?: DropRolesOptions, + RevokeRolesOptions?: RevokeRolesOptions, ) => string | string[] -type TablePrivilege = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE' | 'REFERENCES' | 'TRIGGER' -type SchemaPrivilege = 'CREATE' | 'USAGE' +export type TablePrivilege = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE' | 'REFERENCES' | 'TRIGGER' +export type SchemaPrivilege = 'CREATE' | 'USAGE' interface CommonOnTablesProps { privileges: TablePrivilege | TablePrivilege[] | 'ALL' @@ -68,12 +68,13 @@ type GrantOnTablesFn = (props: GrantOnTablesProps & RevokeOnObjectsProps) => str export type GrantOnTables = GrantOnTablesFn & { reverse: GrantOnTablesFn } export type RevokeOnTables = (props: RevokeOnTablesProps) => string | string[] -interface GrantOnSchemasProps { +export interface OnlyGrantOnSchemasProps { privileges: SchemaPrivilege | SchemaPrivilege[] | 'ALL' schemas: string[] | string roles: Name | Name[] } - -type GrantOnSchemasFn = (props: GrantOnSchemasProps & WithGrantOption & RevokeOnObjectsProps) => string | string[] +export type GrantOnSchemasProps = OnlyGrantOnSchemasProps & WithGrantOption & RevokeOnObjectsProps +export type RevokeOnSchemasProps = OnlyGrantOnSchemasProps & RevokeOnObjectsProps +type GrantOnSchemasFn = (props: GrantOnSchemasProps) => string | string[] export type GrantOnSchemas = GrantOnSchemasFn & { reverse: GrantOnSchemasFn } -export type RevokeOnSchemas = (props: GrantOnSchemasProps & RevokeOnObjectsProps) => string | string[] +export type RevokeOnSchemas = (props: RevokeOnSchemasProps) => string | string[] diff --git a/test/migrations/086_grant_test.js b/test/migrations/086_grant_test.js index 948da91b1..0ff154876 100644 --- a/test/migrations/086_grant_test.js +++ b/test/migrations/086_grant_test.js @@ -2,7 +2,7 @@ const { constants: { schema, table, role1, role2, tablePrivileges, schemaPrivilege }, } = require('./085_grant_tables_schemas_roles') -const hasTablePrivileges = async (pgm, role, tableName, privileges) => { +export const hasTablePrivileges = async (pgm, role, tableName, privileges) => { const rows = await pgm.db.select(` SELECT grantee, privilege_type FROM information_schema.role_table_grants @@ -13,14 +13,14 @@ const hasTablePrivileges = async (pgm, role, tableName, privileges) => { return privileges.reduce((acc, privilege) => acc && foundPrivileges.includes(privilege), true) } -const hasSchemaPrivilege = async (pgm, role, schemaName, privilege) => { +export const hasSchemaPrivilege = async (pgm, role, schemaName, privilege) => { const rows = await pgm.db.select(` SELECT has_schema_privilege('${role}', '${schemaName}', '${privilege}'); `) return rows.length && rows[0].has_schema_privilege } -const isMemberOf = async (pgm, role, roleGroups) => { +export const isMemberOf = async (pgm, role, roleGroups) => { const rows = await pgm.db.select(` SELECT rolname FROM pg_roles WHERE pg_has_role('${role}', oid, 'member') AND rolname <> '${role}'; `) diff --git a/test/migrations/087_revoke_tables_schemas_roles.js b/test/migrations/087_revoke_tables_schemas_roles.js new file mode 100644 index 000000000..038ac7dd2 --- /dev/null +++ b/test/migrations/087_revoke_tables_schemas_roles.js @@ -0,0 +1,17 @@ +const { + constants: { schema, table, role1, role2, tablePrivileges, schemaPrivilege }, +} = require('./085_grant_tables_schemas_roles') + +exports.up = (pgm) => { + pgm.revokeRoles(role1, role2) + pgm.revokeOnSchemas({ + privileges: schemaPrivilege, + schemas: schema, + roles: role1, + }) + pgm.revokeOnTables({ + privileges: tablePrivileges, + tables: table, + roles: role1, + }) +} diff --git a/test/migrations/088_revoke_test.js b/test/migrations/088_revoke_test.js new file mode 100644 index 000000000..a3b51d850 --- /dev/null +++ b/test/migrations/088_revoke_test.js @@ -0,0 +1,22 @@ +const { + constants: { schema, table, role1, role2, tablePrivileges, schemaPrivilege }, +} = require('./085_grant_tables_schemas_roles') + +const { hasTablePrivileges, hasSchemaPrivilege, isMemberOf } = require('./086_grant_test') + +exports.up = async (pgm) => { + const hasGrantedTablePrivileges = await hasTablePrivileges(pgm, role1, table, tablePrivileges) + if (hasGrantedTablePrivileges) { + throw new Error(`${role1}'s table privileges were not revoked`) + } + const hasGrantedSchemaPrivilege = await hasSchemaPrivilege(pgm, role1, schema, schemaPrivilege) + if (hasGrantedSchemaPrivilege) { + throw new Error(`${role1}'s USAGE schema privilege was not revoked`) + } + const isMemberOfRole1 = await isMemberOf(pgm, role2, [role1]) + if (isMemberOfRole1) { + throw new Error(`${role2}'s membership of ${role1} was not revoked`) + } +} + +exports.down = () => null