postgresql_privs: add support a type parameter option for types (#63555)

* postgresql_privs: add support a type parameter option for types

* postgresql_privs: add support a type parameter option for types, add changelog fragment

* postgresql_privs: add support a type parameter option for types, add schema handling

* postgresql_privs: add support a type parameter option for types, fix typo

* postgresql_privs: add support a type parameter option for types, add comment
This commit is contained in:
Andrey Klychkov 2019-10-17 16:59:06 +03:00 committed by John R Barker
parent ae16a840f6
commit 7dd46f7b2d
3 changed files with 306 additions and 8 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- postgresql_privs - add support for TYPE as object types in postgresql_privs module (https://github.com/ansible/ansible/issues/62432).

View file

@ -45,12 +45,13 @@ options:
type:
description:
- Type of database object to set privileges on.
- The `default_privs` choice is available starting at version 2.7.
- The 'foreign_data_wrapper' and 'foreign_server' object types are available from Ansible version '2.8'.
- The C(default_privs) choice is available starting at version 2.7.
- The C(foreign_data_wrapper) and C(foreign_server) object types are available from Ansible version '2.8'.
- The C(type) choice is available from Ansible version '2.10'.
type: str
default: table
choices: [ database, default_privs, foreign_data_wrapper, foreign_server, function,
group, language, table, tablespace, schema, sequence ]
group, language, table, tablespace, schema, sequence, type ]
objs:
description:
- Comma separated list of database objects to set privileges on.
@ -69,8 +70,10 @@ options:
schema:
description:
- Schema that contains the database objects specified via I(objs).
- May only be provided if I(type) is C(table), C(sequence), C(function)
or C(default_privs). Defaults to C(public) in these cases.
- May only be provided if I(type) is C(table), C(sequence), C(function), C(type),
or C(default_privs). Defaults to C(public) in these cases.
- Pay attention, for embedded types when I(type=type)
I(schema) can be C(pg_catalog) or C(information_schema) respectively.
type: str
roles:
description:
@ -326,6 +329,15 @@ EXAMPLES = r'''
type: foreign_data_wrapper
role: reader
# Available since version 2.10
- name: GRANT ALL PRIVILEGES ON TYPE customtype TO reader
postgresql_privs:
db: test
objs: customtype
privs: ALL
type: type
role: reader
# Available since version 2.8
- name: GRANT ALL PRIVILEGES ON FOREIGN SERVER fdw_server TO reader
postgresql_privs:
@ -376,6 +388,16 @@ EXAMPLES = r'''
type: default_privs
role: reader
target_roles: librarian
# Available since version 2.10
- name: Grant type privileges for pg_catalog.numeric type to alice
postgresql_privs:
type: type
roles: alice
privs: ALL
objs: numeric
schema: pg_catalog
db: acme
'''
RETURN = r'''
@ -622,6 +644,13 @@ class Connection(object):
self.cursor.execute(query, (fs,))
return [t[0] for t in self.cursor.fetchall()]
def get_type_acls(self, schema, types):
query = """SELECT t.typacl FROM pg_catalog.pg_type t
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = %s AND t.typname = ANY (%s) ORDER BY typname"""
self.cursor.execute(query, (schema, types))
return [t[0] for t in self.cursor.fetchall()]
# Manipulating privileges
def manipulate_privs(self, obj_type, privs, objs, roles, target_roles,
@ -669,6 +698,8 @@ class Connection(object):
get_status = self.get_foreign_data_wrapper_acls
elif obj_type == 'foreign_server':
get_status = self.get_foreign_server_acls
elif obj_type == 'type':
get_status = partial(self.get_type_acls, schema_qualifier)
else:
raise Error('Unsupported database object type "%s".' % obj_type)
@ -685,7 +716,7 @@ class Connection(object):
except Exception:
raise Error('Illegal function signature: "%s".' % obj)
obj_ids.append('"%s"."%s"(%s' % (schema_qualifier, f, args))
elif obj_type in ['table', 'sequence']:
elif obj_type in ['table', 'sequence', 'type']:
obj_ids = ['"%s"."%s"' % (schema_qualifier, o) for o in objs]
else:
obj_ids = ['"%s"' % o for o in objs]
@ -892,7 +923,8 @@ def main():
'group',
'default_privs',
'foreign_data_wrapper',
'foreign_server']),
'foreign_server',
'type', ]),
objs=dict(required=False, aliases=['obj']),
schema=dict(required=False),
roles=dict(required=True, aliases=['role']),
@ -917,7 +949,7 @@ def main():
# Create type object as namespace for module params
p = type('Params', (), module.params)
# param "schema": default, allowed depends on param "type"
if p.type in ['table', 'sequence', 'function', 'default_privs']:
if p.type in ['table', 'sequence', 'function', 'type', 'default_privs']:
p.schema = p.schema or 'public'
elif p.schema:
module.fail_json(msg='Argument "schema" is not allowed '

View file

@ -677,9 +677,271 @@
login_password: password
ignore_errors: yes
when: postgres_version_resp.stdout is version('10', '>=')
###########################################
# Test for 'type' value of type parameter #
###########################################
# Test
- name: Grant type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: present
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: numeric
schema: pg_catalog
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
- name: Grant type privileges again using check_mode
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: present
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: numeric
schema: pg_catalog
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
check_mode: yes
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is not changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
- name: Grant type privileges again
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: present
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: numeric
schema: pg_catalog
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is not changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
- name: Revoke type privileges in check_mode
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: absent
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: numeric
schema: pg_catalog
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
check_mode: yes
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
- name: Revoke type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: absent
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: numeric
schema: pg_catalog
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: SELECT typacl FROM pg_catalog.pg_type WHERE typname = 'numeric';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' not in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
# type with default schema (public):
- name: Create custom type in schema public
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: "CREATE TYPE compfoo AS (f1 int, f2 text)"
when: postgres_version_resp.stdout is version('10', '>=')
# Test
- name: Grant type privileges with default schema
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: present
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: compfoo
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
register: result
when: postgres_version_resp.stdout is version('10', '>=')
# Checks
- assert:
that:
- result is changed
when: postgres_version_resp.stdout is version('10', '>=')
- name: Get type privileges
become: yes
become_user: "{{ pg_user }}"
postgresql_query:
login_user: "{{ pg_user }}"
login_db: "{{ db_name }}"
query: >
SELECT t.typacl FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n
ON n.oid = t.typnamespace WHERE t.typname = 'compfoo' AND n.nspname = 'public';
register: typ_result
when: postgres_version_resp.stdout is version('10', '>=')
- assert:
that:
- "'{{ db_user2 }}' in typ_result.query_result[0].typacl"
when: postgres_version_resp.stdout is version('10', '>=')
# Cleanup
- name: Remove privs
become: yes
become_user: "{{ pg_user }}"
postgresql_privs:
state: absent
type: type
roles: "{{ db_user2 }}"
privs: ALL
objs: compfoo
db: "{{ db_name }}"
login_user: "{{ pg_user }}"
when: postgres_version_resp.stdout is version('10', '>=')
- name: Reassign ownership
become_user: "{{ pg_user }}"
become: yes
postgresql_owner:
login_user: "{{ pg_user }}"
db: "{{ db_name }}"
new_owner: "{{ pg_user }}"
reassign_owned_by: "{{ item }}"
loop:
- "{{ db_user2 }}"
- "{{ db_user3 }}"
- name: Remove user given permissions
become: yes
become_user: "{{ pg_user }}"
postgresql_user:
name: "{{ db_user2 }}"
state: absent
@ -687,6 +949,8 @@
login_user: "{{ pg_user }}"
- name: Remove user owner of objects
become: yes
become_user: "{{ pg_user }}"
postgresql_user:
name: "{{ db_user3 }}"
state: absent