grant.pp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. # Define for granting permissions to roles. See README.md for more details.
  2. define postgresql::server::grant (
  3. $role,
  4. $db,
  5. $privilege = undef,
  6. $object_type = 'database',
  7. $object_name = undef,
  8. $psql_db = $postgresql::server::default_database,
  9. $psql_user = $postgresql::server::user,
  10. $port = $postgresql::server::port,
  11. $onlyif_exists = false,
  12. $connect_settings = $postgresql::server::default_connect_settings,
  13. ) {
  14. $group = $postgresql::server::group
  15. $psql_path = $postgresql::server::psql_path
  16. if ! $object_name {
  17. $_object_name = $db
  18. } else {
  19. $_object_name = $object_name
  20. }
  21. validate_bool($onlyif_exists)
  22. #
  23. # Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port
  24. #
  25. if $port != undef {
  26. $port_override = $port
  27. } elsif $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
  28. $port_override = undef
  29. } else {
  30. $port_override = $postgresql::server::port
  31. }
  32. ## Munge the input values
  33. $_object_type = upcase($object_type)
  34. $_privilege = upcase($privilege)
  35. ## Validate that the object type is known
  36. validate_string($_object_type,
  37. #'COLUMN',
  38. 'DATABASE',
  39. #'FOREIGN SERVER',
  40. #'FOREIGN DATA WRAPPER',
  41. #'FUNCTION',
  42. #'PROCEDURAL LANGUAGE',
  43. 'SCHEMA',
  44. 'SEQUENCE',
  45. 'ALL SEQUENCES IN SCHEMA',
  46. 'TABLE',
  47. 'ALL TABLES IN SCHEMA',
  48. #'TABLESPACE',
  49. #'VIEW',
  50. )
  51. # You can use ALL TABLES IN SCHEMA by passing schema_name to object_name
  52. # You can use ALL SEQUENCES IN SCHEMA by passing schema_name to object_name
  53. ## Validate that the object type's privilege is acceptable
  54. # TODO: this is a terrible hack; if they pass "ALL" as the desired privilege,
  55. # we need a way to test for it--and has_database_privilege does not
  56. # recognize 'ALL' as a valid privilege name. So we probably need to
  57. # hard-code a mapping between 'ALL' and the list of actual privileges that
  58. # it entails, and loop over them to check them. That sort of thing will
  59. # probably need to wait until we port this over to ruby, so, for now, we're
  60. # just going to assume that if they have "CREATE" privileges on a database,
  61. # then they have "ALL". (I told you that it was terrible!)
  62. case $_object_type {
  63. 'DATABASE': {
  64. $unless_privilege = $_privilege ? {
  65. 'ALL' => 'CREATE',
  66. 'ALL PRIVILEGES' => 'CREATE',
  67. default => $_privilege,
  68. }
  69. validate_string($unless_privilege,'CREATE','CONNECT','TEMPORARY','TEMP',
  70. 'ALL','ALL PRIVILEGES')
  71. $unless_function = 'has_database_privilege'
  72. $on_db = $psql_db
  73. $onlyif_function = undef
  74. }
  75. 'SCHEMA': {
  76. $unless_privilege = $_privilege ? {
  77. 'ALL' => 'CREATE',
  78. 'ALL PRIVILEGES' => 'CREATE',
  79. default => $_privilege,
  80. }
  81. validate_string($_privilege, 'CREATE', 'USAGE', 'ALL', 'ALL PRIVILEGES')
  82. $unless_function = 'has_schema_privilege'
  83. $on_db = $db
  84. $onlyif_function = undef
  85. }
  86. 'SEQUENCE': {
  87. $unless_privilege = $_privilege ? {
  88. 'ALL' => 'USAGE',
  89. default => $_privilege,
  90. }
  91. validate_string($unless_privilege,'USAGE','ALL','ALL PRIVILEGES')
  92. $unless_function = 'has_sequence_privilege'
  93. $on_db = $db
  94. $onlyif_function = undef
  95. }
  96. 'ALL SEQUENCES IN SCHEMA': {
  97. validate_string($_privilege,'USAGE','ALL','ALL PRIVILEGES')
  98. $unless_function = 'custom'
  99. $on_db = $db
  100. $onlyif_function = undef
  101. $schema = $object_name
  102. $custom_privilege = $_privilege ? {
  103. 'ALL' => 'USAGE',
  104. 'ALL PRIVILEGES' => 'USAGE',
  105. default => $_privilege,
  106. }
  107. # This checks if there is a difference between the sequences in the
  108. # specified schema and the sequences for which the role has the specified
  109. # privilege. It uses the EXCEPT clause which computes the set of rows
  110. # that are in the result of the first SELECT statement but not in the
  111. # result of the second one. It then counts the number of rows from this
  112. # operation. If this number is zero then the role has the specified
  113. # privilege for all sequences in the schema and the whole query returns a
  114. # single row, which satisfies the `unless` parameter of Postgresql_psql.
  115. # If this number is not zero then there is at least one sequence for which
  116. # the role does not have the specified privilege, making it necessary to
  117. # execute the GRANT statement.
  118. $custom_unless = "SELECT 1 FROM (
  119. SELECT sequence_name
  120. FROM information_schema.sequences
  121. WHERE sequence_schema='${schema}'
  122. EXCEPT DISTINCT
  123. SELECT object_name as sequence_name
  124. FROM information_schema.role_usage_grants
  125. WHERE object_type='SEQUENCE'
  126. AND grantee='${role}'
  127. AND object_schema='${schema}'
  128. AND privilege_type='${custom_privilege}'
  129. ) P
  130. HAVING count(P.sequence_name) = 0"
  131. }
  132. 'TABLE': {
  133. $unless_privilege = $_privilege ? {
  134. 'ALL' => 'INSERT',
  135. default => $_privilege,
  136. }
  137. validate_string($unless_privilege,'SELECT','INSERT','UPDATE','DELETE',
  138. 'TRUNCATE','REFERENCES','TRIGGER','ALL','ALL PRIVILEGES')
  139. $unless_function = 'has_table_privilege'
  140. $on_db = $db
  141. $onlyif_function = $onlyif_exists ? {
  142. true => 'table_exists',
  143. default => undef,
  144. }
  145. }
  146. 'ALL TABLES IN SCHEMA': {
  147. validate_string($_privilege,'SELECT','INSERT','UPDATE','DELETE',
  148. 'TRUNCATE','REFERENCES','TRIGGER','ALL','ALL PRIVILEGES')
  149. $unless_function = 'custom'
  150. $on_db = $db
  151. $onlyif_function = undef
  152. $schema = $object_name
  153. # Again there seems to be no easy way in plain SQL to check if ALL
  154. # PRIVILEGES are granted on a table. By convention we use INSERT
  155. # here to represent ALL PRIVILEGES (truly terrible).
  156. $custom_privilege = $_privilege ? {
  157. 'ALL' => 'INSERT',
  158. 'ALL PRIVILEGES' => 'INSERT',
  159. default => $_privilege,
  160. }
  161. # This checks if there is a difference between the tables in the
  162. # specified schema and the tables for which the role has the specified
  163. # privilege. It uses the EXCEPT clause which computes the set of rows
  164. # that are in the result of the first SELECT statement but not in the
  165. # result of the second one. It then counts the number of rows from this
  166. # operation. If this number is zero then the role has the specified
  167. # privilege for all tables in the schema and the whole query returns a
  168. # single row, which satisfies the `unless` parameter of Postgresql_psql.
  169. # If this number is not zero then there is at least one table for which
  170. # the role does not have the specified privilege, making it necessary to
  171. # execute the GRANT statement.
  172. $custom_unless = "SELECT 1 FROM (
  173. SELECT table_name
  174. FROM information_schema.tables
  175. WHERE table_schema='${schema}'
  176. EXCEPT DISTINCT
  177. SELECT table_name
  178. FROM information_schema.role_table_grants
  179. WHERE grantee='${role}'
  180. AND table_schema='${schema}'
  181. AND privilege_type='${custom_privilege}'
  182. ) P
  183. HAVING count(P.table_name) = 0"
  184. }
  185. default: {
  186. fail("Missing privilege validation for object type ${_object_type}")
  187. }
  188. }
  189. # This is used to give grant to "schemaname"."tablename"
  190. # If you need such grant, use:
  191. # postgresql::grant { 'table:foo':
  192. # role => 'joe',
  193. # ...
  194. # object_type => 'TABLE',
  195. # object_name => [$schema, $table],
  196. # }
  197. if is_array($_object_name) {
  198. $_togrant_object = join($_object_name, '"."')
  199. # Never put double quotes into has_*_privilege function
  200. $_granted_object = join($_object_name, '.')
  201. } else {
  202. $_granted_object = $_object_name
  203. $_togrant_object = $_object_name
  204. }
  205. $_unless = $unless_function ? {
  206. false => undef,
  207. 'custom' => $custom_unless,
  208. default => "SELECT 1 WHERE ${unless_function}('${role}',
  209. '${_granted_object}', '${unless_privilege}')",
  210. }
  211. $_onlyif = $onlyif_function ? {
  212. 'table_exists' => "SELECT true FROM pg_tables WHERE tablename = '${_togrant_object}'",
  213. default => undef,
  214. }
  215. $grant_cmd = "GRANT ${_privilege} ON ${_object_type} \"${_togrant_object}\" TO
  216. \"${role}\""
  217. postgresql_psql { "grant:${name}":
  218. command => $grant_cmd,
  219. db => $on_db,
  220. port => $port_override,
  221. connect_settings => $connect_settings,
  222. psql_user => $psql_user,
  223. psql_group => $group,
  224. psql_path => $psql_path,
  225. unless => $_unless,
  226. onlyif => $_onlyif,
  227. require => Class['postgresql::server']
  228. }
  229. if($role != undef and defined(Postgresql::Server::Role[$role])) {
  230. Postgresql::Server::Role[$role]->Postgresql_psql["grant:${name}"]
  231. }
  232. if($db != undef and defined(Postgresql::Server::Database[$db])) {
  233. Postgresql::Server::Database[$db]->Postgresql_psql["grant:${name}"]
  234. }
  235. }