X-Git-Url: https://git.adam-barratt.org.uk/?a=blobdiff_plain;f=3rdparty%2Fmodules%2Fpostgresql%2Fmanifests%2Fserver%2Fgrant.pp;fp=3rdparty%2Fmodules%2Fpostgresql%2Fmanifests%2Fserver%2Fgrant.pp;h=f030019b798176fd54c3bbbf41970bd564f143ff;hb=a69999e580f8b3abd12446c2d6ad59e517651813;hp=0000000000000000000000000000000000000000;hpb=e7b6b352165009c385c52fcfe5a1055690dbfa4b;p=mirror%2Fdsa-puppet.git diff --git a/3rdparty/modules/postgresql/manifests/server/grant.pp b/3rdparty/modules/postgresql/manifests/server/grant.pp new file mode 100644 index 000000000..f030019b7 --- /dev/null +++ b/3rdparty/modules/postgresql/manifests/server/grant.pp @@ -0,0 +1,294 @@ +# Define for granting permissions to roles. See README.md for more details. +define postgresql::server::grant ( + String $role, + String $db, + Optional[String] $privilege = undef, + String $object_type = 'database', + Optional[String[1]] $object_name = undef, + String $psql_db = $postgresql::server::default_database, + String $psql_user = $postgresql::server::user, + Integer $port = $postgresql::server::port, + Boolean $onlyif_exists = false, + Hash $connect_settings = $postgresql::server::default_connect_settings, +) { + + $group = $postgresql::server::group + $psql_path = $postgresql::server::psql_path + + if ! $object_name { + $_object_name = $db + } else { + $_object_name = $object_name + } + + # + # Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port + # + if $port != undef { + $port_override = $port + } elsif $connect_settings != undef and has_key( $connect_settings, 'PGPORT') { + $port_override = undef + } else { + $port_override = $postgresql::server::port + } + + ## Munge the input values + $_object_type = upcase($object_type) + $_privilege = upcase($privilege) + + ## Validate that the object type is known + validate_re($_object_type,[ + #'^COLUMN$', + '^DATABASE$', + #'^FOREIGN SERVER$', + #'^FOREIGN DATA WRAPPER$', + #'^FUNCTION$', + #'^PROCEDURAL LANGUAGE$', + '^SCHEMA$', + '^SEQUENCE$', + '^ALL SEQUENCES IN SCHEMA$', + '^TABLE$', + '^ALL TABLES IN SCHEMA$', + '^LANGUAGE$', + #'^TABLESPACE$', + #'^VIEW$', + ] + ) + # You can use ALL TABLES IN SCHEMA by passing schema_name to object_name + # You can use ALL SEQUENCES IN SCHEMA by passing schema_name to object_name + + ## Validate that the object type's privilege is acceptable + # TODO: this is a terrible hack; if they pass "ALL" as the desired privilege, + # we need a way to test for it--and has_database_privilege does not + # recognize 'ALL' as a valid privilege name. So we probably need to + # hard-code a mapping between 'ALL' and the list of actual privileges that + # it entails, and loop over them to check them. That sort of thing will + # probably need to wait until we port this over to ruby, so, for now, we're + # just going to assume that if they have "CREATE" privileges on a database, + # then they have "ALL". (I told you that it was terrible!) + case $_object_type { + 'DATABASE': { + $unless_privilege = $_privilege ? { + 'ALL' => 'CREATE', + 'ALL PRIVILEGES' => 'CREATE', + default => $_privilege, + } + validate_re($unless_privilege, [ '^$', '^CREATE$','^CONNECT$','^TEMPORARY$','^TEMP$', + '^ALL$','^ALL PRIVILEGES$' ]) + $unless_function = 'has_database_privilege' + $on_db = $psql_db + $onlyif_function = undef + } + 'SCHEMA': { + $unless_privilege = $_privilege ? { + 'ALL' => 'CREATE', + 'ALL PRIVILEGES' => 'CREATE', + default => $_privilege, + } + validate_re($_privilege, [ '^$', '^CREATE$', '^USAGE$', '^ALL$', '^ALL PRIVILEGES$' ]) + $unless_function = 'has_schema_privilege' + $on_db = $db + $onlyif_function = undef + } + 'SEQUENCE': { + $unless_privilege = $_privilege ? { + 'ALL' => 'USAGE', + default => $_privilege, + } + validate_re($unless_privilege, [ '^$', '^USAGE$', '^SELECT$', '^UPDATE$', '^ALL$', '^ALL PRIVILEGES$' ]) + $unless_function = 'has_sequence_privilege' + $on_db = $db + $onlyif_function = undef + } + 'ALL SEQUENCES IN SCHEMA': { + validate_re($_privilege, [ '^$', '^USAGE$', '^SELECT$', '^UPDATE$', '^ALL$', '^ALL PRIVILEGES$' ]) + $unless_function = 'custom' + $on_db = $db + $onlyif_function = undef + + $schema = $object_name + + $custom_privilege = $_privilege ? { + 'ALL' => 'USAGE', + 'ALL PRIVILEGES' => 'USAGE', + default => $_privilege, + } + + # This checks if there is a difference between the sequences in the + # specified schema and the sequences for which the role has the specified + # privilege. It uses the EXCEPT clause which computes the set of rows + # that are in the result of the first SELECT statement but not in the + # result of the second one. It then counts the number of rows from this + # operation. If this number is zero then the role has the specified + # privilege for all sequences in the schema and the whole query returns a + # single row, which satisfies the `unless` parameter of Postgresql_psql. + # If this number is not zero then there is at least one sequence for which + # the role does not have the specified privilege, making it necessary to + # execute the GRANT statement. + $custom_unless = "SELECT 1 FROM ( + SELECT sequence_name + FROM information_schema.sequences + WHERE sequence_schema='${schema}' + EXCEPT DISTINCT + SELECT object_name as sequence_name + FROM ( + SELECT object_schema, + object_name, + grantee, + CASE privs_split + WHEN 'r' THEN 'SELECT' + WHEN 'w' THEN 'UPDATE' + WHEN 'U' THEN 'USAGE' + END AS privilege_type + FROM ( + SELECT DISTINCT + object_schema, + object_name, + (regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[1] AS grantee, + regexp_split_to_table((regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[2],E'\\s*') AS privs_split + FROM ( + SELECT n.nspname as object_schema, + c.relname as object_name, + regexp_split_to_table(array_to_string(c.relacl,','),',') AS privs + FROM pg_catalog.pg_class c + LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE c.relkind = 'S' + AND n.nspname NOT IN ( 'pg_catalog', 'information_schema' ) + ) P1 + ) P2 + ) P3 + WHERE grantee='${role}' + AND object_schema='${schema}' + AND privilege_type='${custom_privilege}' + ) P + HAVING count(P.sequence_name) = 0" + } + 'TABLE': { + $unless_privilege = $_privilege ? { + 'ALL' => 'INSERT', + default => $_privilege, + } + validate_re($unless_privilege,[ '^$', '^SELECT$','^INSERT$','^UPDATE$','^DELETE$', + '^TRUNCATE$','^REFERENCES$','^TRIGGER$','^ALL$','^ALL PRIVILEGES$' ]) + $unless_function = 'has_table_privilege' + $on_db = $db + $onlyif_function = $onlyif_exists ? { + true => 'table_exists', + default => undef, + } + } + 'ALL TABLES IN SCHEMA': { + validate_re($_privilege, [ '^$', '^SELECT$','^INSERT$','^UPDATE$','^DELETE$', + '^TRUNCATE$','^REFERENCES$','^TRIGGER$','^ALL$','^ALL PRIVILEGES$' ]) + $unless_function = 'custom' + $on_db = $db + $onlyif_function = undef + + $schema = $object_name + + # Again there seems to be no easy way in plain SQL to check if ALL + # PRIVILEGES are granted on a table. By convention we use INSERT + # here to represent ALL PRIVILEGES (truly terrible). + $custom_privilege = $_privilege ? { + 'ALL' => 'INSERT', + 'ALL PRIVILEGES' => 'INSERT', + default => $_privilege, + } + + # This checks if there is a difference between the tables in the + # specified schema and the tables for which the role has the specified + # privilege. It uses the EXCEPT clause which computes the set of rows + # that are in the result of the first SELECT statement but not in the + # result of the second one. It then counts the number of rows from this + # operation. If this number is zero then the role has the specified + # privilege for all tables in the schema and the whole query returns a + # single row, which satisfies the `unless` parameter of Postgresql_psql. + # If this number is not zero then there is at least one table for which + # the role does not have the specified privilege, making it necessary to + # execute the GRANT statement. + $custom_unless = "SELECT 1 FROM ( + SELECT table_name + FROM information_schema.tables + WHERE table_schema='${schema}' + EXCEPT DISTINCT + SELECT table_name + FROM information_schema.role_table_grants + WHERE grantee='${role}' + AND table_schema='${schema}' + AND privilege_type='${custom_privilege}' + ) P + HAVING count(P.table_name) = 0" + } + 'LANGUAGE': { + $unless_privilege = $_privilege ? { + 'ALL' => 'USAGE', + 'ALL PRIVILEGES' => 'USAGE', + default => $_privilege, + } + validate_re($unless_privilege, [ '^$','^CREATE$','^USAGE$','^ALL$','^ALL PRIVILEGES$' ]) + $unless_function = 'has_language_privilege' + $on_db = $db + $onlyif_function = $onlyif_exists ? { + true => 'language_exists', + default => undef, + } + } + + default: { + fail("Missing privilege validation for object type ${_object_type}") + } + } + + # This is used to give grant to "schemaname"."tablename" + # If you need such grant, use: + # postgresql::grant { 'table:foo': + # role => 'joe', + # ... + # object_type => 'TABLE', + # object_name => [$schema, $table], + # } + if is_array($_object_name) { + $_togrant_object = join($_object_name, '"."') + # Never put double quotes into has_*_privilege function + $_granted_object = join($_object_name, '.') + } else { + $_granted_object = $_object_name + $_togrant_object = $_object_name + } + + $_unless = $unless_function ? { + false => undef, + 'custom' => $custom_unless, + default => "SELECT 1 WHERE ${unless_function}('${role}', + '${_granted_object}', '${unless_privilege}')", + } + + $_onlyif = $onlyif_function ? { + 'table_exists' => "SELECT true FROM pg_tables WHERE tablename = '${_togrant_object}'", + 'language_exists' => "SELECT true from pg_language WHERE lanname = '${_togrant_object}'", + default => undef, + } + + $grant_cmd = "GRANT ${_privilege} ON ${_object_type} \"${_togrant_object}\" TO + \"${role}\"" + postgresql_psql { "grant:${name}": + command => $grant_cmd, + db => $on_db, + port => $port_override, + connect_settings => $connect_settings, + psql_user => $psql_user, + psql_group => $group, + psql_path => $psql_path, + unless => $_unless, + onlyif => $_onlyif, + require => Class['postgresql::server'] + } + + if($role != undef and defined(Postgresql::Server::Role[$role])) { + Postgresql::Server::Role[$role]->Postgresql_psql["grant:${name}"] + } + + if($db != undef and defined(Postgresql::Server::Database[$db])) { + Postgresql::Server::Database[$db]->Postgresql_psql["grant:${name}"] + } +}