password_expire support for mysql_user (#598)

* initial commit for password_expire support

* sanity check and default values

* add one more if block for version check

* some changes and integration tests

* docs and sanity and integration test fix

* make integration tests work

* make integration tests work

* fix unneeded commits

* fix verify as well

* Update plugins/modules/mysql_user.py

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>

* Update tests/integration/targets/test_mysql_user/tasks/test_password_expire.yml

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>

* Apply suggestions from code review

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>

* Update plugins/modules/mysql_user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/mysql_user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/mysql_user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/mysql_user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/module_utils/user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/module_utils/user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/module_utils/user.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* typo and no_log remove for password_expire* vars

* add change log fragment

* move one if statement to module initialiazation

* fix merge conflicts

* fix order

* some fixes

* set no_log to true for password word containing keys

* fix sanity error

* Update changelogs/fragments/598-password_expire-support-for-mysql_user.yml

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

---------

Co-authored-by: Laurent Indermühle <laurent.indermuehle@pm.me>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
This commit is contained in:
tompal3 2024-02-22 11:31:01 +02:00 committed by GitHub
commit 40af258d86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 375 additions and 6 deletions

View file

@ -43,6 +43,8 @@
- include_tasks: test_idempotency.yml
- include_tasks: test_password_expire.yml
# ============================================================
# Create user with no privileges and verify default privileges are assign
#

View file

@ -0,0 +1,174 @@
---
# Tests scenarios for password_expire
- vars:
mysql_parameters: &mysql_params
login_user: "{{ mysql_user }}"
login_password: "{{ mysql_password }}"
login_host: "{{ mysql_host }}"
login_port: "{{ mysql_primary_port }}"
block:
- include_tasks: utils/assert_user_password_expire.yml
vars:
username: "{{ item.username }}"
host: "{{ item.host | default('localhost')}}"
password_expire: "{{ item.password_expire }}"
password: "{{ user_password_1 }}"
expect_change: "{{ item.expect_change }}"
expect_password_expire_change: "{{ item.expect_password_expire_change }}"
expected_password_lifetime: "{{ item.expected_password_lifetime }}"
password_expire_interval: "{{ item.password_expire_interval | default(omit) }}"
expected_password_expired: "{{ item.expected_password_expired }}"
check_mode: "{{ item.check_mode | default(omit) }}"
loop:
# all variants set the password when nothing exists
# never expires
- username: "{{ user_name_1 }}"
host: "%"
password_expire: never
expect_change: true
expected_password_lifetime: "0"
expected_password_expired: "N"
# expires ussing default policy
- username: "{{ user_name_2 }}"
password_expire: default
expect_change: true
expected_password_lifetime: "-1"
expected_password_expired: "N"
# expires ussing interval
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: "10"
expect_change: true
expected_password_lifetime: "10"
expected_password_expired: "N"
# assert idempotency
- username: "{{ user_name_1 }}"
host: "%"
password_expire: never
expect_change: false
expected_password_lifetime: "0"
expected_password_expired: "N"
- username: "{{ user_name_2 }}"
password_expire: default
expect_change: false
expected_password_lifetime: "-1"
expected_password_expired: "N"
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: "10"
expect_change: false
expected_password_lifetime: "10"
expected_password_expired: "N"
# assert change is made
- username: "{{ user_name_3 }}"
password_expire: never
expect_change: true
expected_password_lifetime: "0"
expected_password_expired: "N"
- username: "{{ user_name_1 }}"
host: "%"
password_expire: default
expect_change: true
expected_password_lifetime: "-1"
expected_password_expired: "N"
- username: "{{ user_name_2 }}"
password_expire: interval
password_expire_interval: "100"
expect_change: true
expected_password_lifetime: "100"
expected_password_expired: "N"
# assert password expires now
- username: "{{ user_name_1 }}"
host: "%"
password_expire: now
expect_change: true
expected_password_lifetime: "-1" # password lifetime should be the same
expected_password_expired: "Y"
- username: "{{ user_name_2 }}"
password_expire: now
expect_change: true
expected_password_lifetime: "100" # password lifetime should be the same
expected_password_expired: "Y"
# assert idempotency password expires now
- username: "{{ user_name_1 }}"
host: "%"
password_expire: now
expect_change: false
expected_password_lifetime: "-1" # password lifetime should be the same
expected_password_expired: "Y"
- username: "{{ user_name_2 }}"
password_expire: now
expect_change: false
expected_password_lifetime: "100" # password lifetime should be the same
expected_password_expired: "Y"
# assert check_mode
- username: "{{ user_name_3 }}"
password_expire: interval
password_expire_interval: 10
check_mode: true
expect_change: false
expected_password_lifetime: "0"
expected_password_expired: "N"
- name: password_expire | Set password_expire = interval without password_expire_interval
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
state: present
register: result
ignore_errors: true
- name: password_expire | Assert that action fails if 'password_expire_interval' not set
ansible.builtin.assert:
that:
- result is failed
- name: password_expire | Set password_expire_interval < 1
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
password_expire_interval: -1
state: present
register: result
ignore_errors: true
- name: password_expire | Assert that action fails if 'password_expire_interval' is < 1
ansible.builtin.assert:
that:
- result is failed
- "'should be positive number' in result.msg"
- name: password_expire | check mode for user creation
community.mysql.mysql_user:
<<: *mysql_params
name: '{{ user_name_4 }}'
host: '%'
password: '{{ user_password_4 }}'
password_expire: interval
password_expire_interval: 20
state: present
register: result
check_mode: True
failed_when: result is changed
- include_tasks: utils/remove_user.yml
vars:
user_name: "{{ item.username }}"
loop:
- username: "{{ user_name_1 }}"
- username: "{{ user_name_2 }}"
- username: "{{ user_name_3 }}"
- username: "{{ user_name_4 }}"

View file

@ -0,0 +1,56 @@
---
- name: Utils | Assert user password_expire | Create modify {{ username }} with password_expire
community.mysql.mysql_user:
login_user: "{{ mysql_parameters.login_user }}"
login_password: "{{ mysql_parameters.login_password }}"
login_host: "{{ mysql_parameters.login_host }}"
login_port: "{{ mysql_parameters.login_port }}"
state: present
name: "{{ username }}"
host: "{{ host }}"
password: "{{ password }}"
password_expire: "{{ password_expire }}"
password_expire_interval: "{{ password_expire_interval | default(omit) }}"
register: result
check_mode: "{{ check_mode | default(false) }}"
failed_when: result.changed != expect_change_value
vars:
expect_change_value: "{{ expect_change }}"
- name: Utils | Assert user password_lifetime | Query user '{{ username }}'
ansible.builtin.command:
cmd: >
{{ mysql_command }} -BNe "SELECT IFNULL(password_lifetime, -1)
FROM mysql.user where user='{{ username }}' and host='{{ host }}'"
register: password_lifetime
when:
- db_engine == 'mysql'
- db_version is version('5.7.0', '>=')
failed_when: expected_password_lifetime_value not in password_lifetime.stdout_lines
vars:
expected_password_lifetime_value: "{{ expected_password_lifetime }}"
- name: Utils | Assert user password_lifetime | Query user '{{ username }}'
ansible.builtin.command:
"{{ mysql_command }} -BNe \"SELECT JSON_EXTRACT(Priv, '$.password_lifetime') AS password_lifetime \
FROM mysql.global_priv \
WHERE user='{{ username }}' and host='{{ host }}'\""
register: password_lifetime
when:
- db_engine == 'mariadb'
- db_version is version('10.4.3', '>=')
failed_when: expected_password_lifetime_value not in password_lifetime.stdout_lines
vars:
expected_password_lifetime_value: "{{ expected_password_lifetime }}"
- name: Utils | Assert user password_expired | Query user '{{ username }}'
ansible.builtin.command:
cmd: >
{{ mysql_command }} -BNe "SELECT password_expired FROM mysql.user
WHERE user='{{ username }}' and host='{{ host }}'"
register: password_expired
when: (db_engine == 'mysql' and db_version is version('5.7.0', '>=')) or
(db_engine == 'mariadb' and db_version is version('10.4.3', '>='))
failed_when: expected_password_expired_value not in password_expired.stdout_lines
vars:
expected_password_expired_value: "{{ expected_password_expired }}"