# Setup - name: Create DB become_user: "{{ pg_user }}" become: yes postgresql_db: state: present name: "{{ db_name }}" login_user: "{{ pg_user }}" - name: Create a user to be owner of objects postgresql_user: name: "{{ db_user3 }}" state: present encrypted: yes password: password role_attr_flags: CREATEDB,LOGIN db: "{{ db_name }}" login_user: "{{ pg_user }}" - name: Create a user to be given permissions and other tests postgresql_user: name: "{{ db_user2 }}" state: present encrypted: yes password: password role_attr_flags: LOGIN db: "{{ db_name }}" login_user: "{{ pg_user }}" ############################# # Test of solving bug 27327 # ############################# # Create the test table and view: - name: Create table become: yes become_user: "{{ pg_user }}" postgresql_table: login_user: "{{ pg_user }}" db: postgres name: test_table1 columns: - id int - name: Create view become: yes become_user: "{{ pg_user }}" postgresql_query: login_user: "{{ pg_user }}" db: postgres query: "CREATE VIEW test_view AS SELECT id FROM test_table1" # Test check_mode: - name: Grant SELECT on test_view, check_mode become: yes become_user: "{{ pg_user }}" postgresql_privs: login_user: "{{ pg_user }}" db: postgres state: present privs: SELECT type: table objs: test_view roles: "{{ db_user2 }}" trust_input: no check_mode: yes register: result - assert: that: - result is changed # Check: - name: Check that nothing was changed after the prev step become: yes become_user: "{{ pg_user }}" postgresql_query: login_user: "{{ pg_user }}" db: postgres query: "SELECT grantee FROM information_schema.role_table_grants WHERE table_name='test_view' AND grantee = '{{ db_user2 }}'" register: result - assert: that: - result.rowcount == 0 # Test true mode: - name: Grant SELECT on test_view become: yes become_user: "{{ pg_user }}" postgresql_privs: login_user: "{{ pg_user }}" db: postgres state: present privs: SELECT type: table objs: test_view roles: "{{ db_user2 }}" trust_input: no register: result - assert: that: - result is changed # Check: - name: Check that nothing was changed after the prev step become: yes become_user: "{{ pg_user }}" postgresql_query: login_user: "{{ pg_user }}" db: postgres query: "SELECT grantee FROM information_schema.role_table_grants WHERE table_name='test_view' AND grantee = '{{ db_user2 }}'" register: result - assert: that: - result.rowcount == 1 # Test true mode: - name: Try to grant SELECT again become: yes become_user: "{{ pg_user }}" postgresql_privs: login_user: "{{ pg_user }}" db: postgres state: present privs: SELECT type: table objs: test_view roles: "{{ db_user2 }}" trust_input: no register: result - assert: that: - result is not changed # Cleanup: - name: Drop test view become: yes become_user: "{{ pg_user }}" postgresql_query: login_user: "{{ pg_user }}" db: postgres query: "DROP VIEW test_view" - name: Drop test table become: yes become_user: "{{ pg_user }}" postgresql_table: login_user: "{{ pg_user }}" db: postgres name: test_table1 state: absent ###################################################### # Test foreign data wrapper and foreign server privs # ###################################################### # Foreign data wrapper setup - name: Create foreign data wrapper extension become: yes become_user: "{{ pg_user }}" shell: echo "CREATE EXTENSION postgres_fdw" | psql -d "{{ db_name }}" - name: Create dummy foreign data wrapper become: yes become_user: "{{ pg_user }}" shell: echo "CREATE FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}" - name: Create foreign server become: yes become_user: "{{ pg_user }}" shell: echo "CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}" # Test - name: Grant foreign data wrapper privileges postgresql_privs: state: present type: foreign_data_wrapper roles: "{{ db_user2 }}" privs: ALL objs: dummy db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is changed - name: Get foreign data wrapper privileges become: yes become_user: "{{ pg_user }}" shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}" vars: fdw_query: > SELECT fdwacl FROM pg_catalog.pg_foreign_data_wrapper WHERE fdwname = ANY (ARRAY['dummy']) ORDER BY fdwname register: fdw_result - assert: that: - "fdw_result.stdout_lines[-1] == '(1 row)'" - "'{{ db_user2 }}' in fdw_result.stdout_lines[-2]" # Test - name: Grant foreign data wrapper privileges second time postgresql_privs: state: present type: foreign_data_wrapper roles: "{{ db_user2 }}" privs: ALL objs: dummy db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is not changed # Test - name: Revoke foreign data wrapper privileges postgresql_privs: state: absent type: foreign_data_wrapper roles: "{{ db_user2 }}" privs: ALL objs: dummy db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is changed - name: Get foreign data wrapper privileges become: yes become_user: "{{ pg_user }}" shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}" vars: fdw_query: > SELECT fdwacl FROM pg_catalog.pg_foreign_data_wrapper WHERE fdwname = ANY (ARRAY['dummy']) ORDER BY fdwname register: fdw_result - assert: that: - "fdw_result.stdout_lines[-1] == '(1 row)'" - "'{{ db_user2 }}' not in fdw_result.stdout_lines[-2]" # Test - name: Revoke foreign data wrapper privileges for second time postgresql_privs: state: absent type: foreign_data_wrapper roles: "{{ db_user2 }}" privs: ALL objs: dummy db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is not changed # Test - name: Grant foreign server privileges postgresql_privs: state: present type: foreign_server roles: "{{ db_user2 }}" privs: ALL objs: dummy_server db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is changed - name: Get foreign server privileges become: yes become_user: "{{ pg_user }}" shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}" vars: fdw_query: > SELECT srvacl FROM pg_catalog.pg_foreign_server WHERE srvname = ANY (ARRAY['dummy_server']) ORDER BY srvname register: fs_result - assert: that: - "fs_result.stdout_lines[-1] == '(1 row)'" - "'{{ db_user2 }}' in fs_result.stdout_lines[-2]" # Test - name: Grant foreign server privileges for second time postgresql_privs: state: present type: foreign_server roles: "{{ db_user2 }}" privs: ALL objs: dummy_server db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is not changed # Test - name: Revoke foreign server privileges postgresql_privs: state: absent type: foreign_server roles: "{{ db_user2 }}" privs: ALL objs: dummy_server db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is changed - name: Get foreign server privileges become: yes become_user: "{{ pg_user }}" shell: echo "{{ fdw_query }}" | psql -d "{{ db_name }}" vars: fdw_query: > SELECT srvacl FROM pg_catalog.pg_foreign_server WHERE srvname = ANY (ARRAY['dummy_server']) ORDER BY srvname register: fs_result - assert: that: - "fs_result.stdout_lines[-1] == '(1 row)'" - "'{{ db_user2 }}' not in fs_result.stdout_lines[-2]" # Test - name: Revoke foreign server privileges for second time postgresql_privs: state: absent type: foreign_server roles: "{{ db_user2 }}" privs: ALL objs: dummy_server db: "{{ db_name }}" login_user: "{{ pg_user }}" trust_input: no register: result ignore_errors: yes # Checks - assert: that: - result is not changed # Foreign data wrapper cleanup - name: Drop foreign server become: yes become_user: "{{ pg_user }}" shell: echo "DROP SERVER dummy_server" | psql -d "{{ db_name }}" - name: Drop dummy foreign data wrapper become: yes become_user: "{{ pg_user }}" shell: echo "DROP FOREIGN DATA WRAPPER dummy" | psql -d "{{ db_name }}" - name: Drop foreign data wrapper extension become: yes become_user: "{{ pg_user }}" shell: echo "DROP EXTENSION postgres_fdw" | psql -d "{{ db_name }}" ########################################## # Test ALL_IN_SCHEMA for 'function' type # ########################################## # Function ALL_IN_SCHEMA Setup - name: Create function for test postgresql_query: query: CREATE FUNCTION public.a() RETURNS integer LANGUAGE SQL AS 'SELECT 2'; db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password # Test - name: Grant execute to all functions postgresql_privs: type: function state: present privs: EXECUTE roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes # Checks - assert: that: result is changed - name: Check that all functions have execute privileges become: yes become_user: "{{ pg_user }}" shell: psql {{ db_name }} -c "SELECT proacl FROM pg_proc WHERE proname = 'a'" -t register: result - assert: that: "'{{ db_user2 }}=X/{{ db_user3 }}' in '{{ result.stdout_lines[0] }}'" # Test - name: Grant execute to all functions again postgresql_privs: type: function state: present privs: EXECUTE roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes # Checks - assert: that: result is not changed # Test - name: Revoke execute to all functions postgresql_privs: type: function state: absent privs: EXECUTE roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes # Checks - assert: that: result is changed # Test - name: Revoke execute to all functions again postgresql_privs: type: function state: absent privs: EXECUTE roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes - assert: that: result is not changed # Function ALL_IN_SCHEMA cleanup - name: Remove function for test postgresql_query: query: DROP FUNCTION public.a(); db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password ################################################# # Test ALL_IN_SCHEMA for 'partioned tables type # ################################################# # Partitioning tables is a feature introduced in Postgresql 10. # (see https://www.postgresql.org/docs/10/ddl-partitioning.html ) # The test below check for this version # Function ALL_IN_SCHEMA Setup - name: Create partioned table for test purpose postgresql_query: query: CREATE TABLE public.testpt (id int not null, logdate date not null) PARTITION BY RANGE (logdate); db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password when: postgres_version_resp.stdout is version('10', '>=') # Test - name: Grant execute to all tables in check mode postgresql_privs: type: table state: present privs: SELECT roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes when: postgres_version_resp.stdout is version('10', '>=') check_mode: yes # Checks - name: Check that all partitioned tables don't have select privileges after the check mode task postgresql_query: query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s db: "{{ db_name }}" login_user: '{{ db_user2 }}' login_password: password named_args: grantuser: '{{ db_user2 }}' become: yes become_user: "{{ pg_user }}" register: result when: postgres_version_resp.stdout is version('10', '>=') - assert: that: - result.rowcount == 0 when: postgres_version_resp.stdout is version('10', '>=') # Test - name: Grant execute to all tables postgresql_privs: type: table state: present privs: SELECT roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes when: postgres_version_resp.stdout is version('10', '>=') # Checks - assert: that: result is changed when: postgres_version_resp.stdout is version('10', '>=') - name: Check that all partitioned tables have select privileges postgresql_query: query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s db: "{{ db_name }}" login_user: '{{ db_user2 }}' login_password: password named_args: grantuser: '{{ db_user2 }}' become: yes become_user: "{{ pg_user }}" register: result when: postgres_version_resp.stdout is version('10', '>=') - assert: that: - result.rowcount == 1 when: postgres_version_resp.stdout is version('10', '>=') # Test - name: Grant execute to all tables again to see no changes are reported postgresql_privs: type: table state: present privs: SELECT roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes when: postgres_version_resp.stdout is version('10', '>=') # Checks - assert: that: result is not changed when: postgres_version_resp.stdout is version('10', '>=') # Test - name: Revoke SELECT to all tables postgresql_privs: type: table state: absent privs: SELECT roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes when: postgres_version_resp.stdout is version('10', '>=') # Checks - assert: that: result is changed when: postgres_version_resp.stdout is version('10', '>=') - name: Check that all partitioned tables don't have select privileges postgresql_query: query: SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='testpt' and privilege_type='SELECT' and grantee = %(grantuser)s db: "{{ db_name }}" login_user: '{{ db_user2 }}' login_password: password named_args: grantuser: '{{ db_user2 }}' become: yes become_user: "{{ pg_user }}" register: result when: postgres_version_resp.stdout is version('10', '>=') - assert: that: - result.rowcount == 0 when: postgres_version_resp.stdout is version('10', '>=') # Test - name: Revoke SELECT to all tables and no changes are reported postgresql_privs: type: table state: absent privs: SELECT roles: "{{ db_user2 }}" objs: ALL_IN_SCHEMA schema: public db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no register: result ignore_errors: yes when: postgres_version_resp.stdout is version('10', '>=') - assert: that: result is not changed when: postgres_version_resp.stdout is version('10', '>=') # Table ALL_IN_SCHEMA cleanup - name: Remove table for test postgresql_query: query: DROP TABLE public.testpt; db: "{{ db_name }}" login_user: "{{ db_user3 }}" login_password: password trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 }}" trust_input: no 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 db: "{{ db_name }}" login_user: "{{ pg_user }}" - name: Remove user owner of objects become: yes become_user: "{{ pg_user }}" postgresql_user: name: "{{ db_user3 }}" state: absent db: "{{ db_name }}" login_user: "{{ pg_user }}" - name: Destroy DB become_user: "{{ pg_user }}" become: yes postgresql_db: state: absent name: "{{ db_name }}" login_user: "{{ pg_user }}"