Skip to content

Commit

Permalink
Merge branch 'main' of github.com:ansible-collections/community.mysql…
Browse files Browse the repository at this point in the history
… into mysql-user-query-refact
  • Loading branch information
steveteahan committed Jan 14, 2021
2 parents 9900b11 + 0690771 commit 389eef0
Show file tree
Hide file tree
Showing 13 changed files with 779 additions and 201 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/79-mysql-user-tests-and-fixes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- mysql_user - fixed creating user with encrypted password in MySQL 8.0 (https://github.com/ansible-collections/community.mysql/pull/79).
2 changes: 2 additions & 0 deletions changelogs/fragments/87-mysql_user_show_routine_support.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- mysql_user - add ``SHOW_ROUTINE`` privilege support (https://github.com/ansible-collections/community.mysql/issues/86).
13 changes: 13 additions & 0 deletions plugins/module_utils/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,16 @@ def mysql_common_argument_spec():
ca_cert=dict(type='path', aliases=['ssl_ca']),
check_hostname=dict(type='bool', default=None),
)


def get_server_version(cursor):
"""Returns a string representation of the server version."""
cursor.execute("SELECT VERSION() AS version")
result = cursor.fetchone()

if isinstance(result, dict):
version_str = result['version']
else:
version_str = result[0]

return version_str
27 changes: 23 additions & 4 deletions plugins/modules/mysql_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = r'''
---
module: mysql_user
Expand Down Expand Up @@ -299,10 +298,13 @@

import re
import string
from distutils.version import LooseVersion

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError
from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
from ansible_collections.community.mysql.plugins.module_utils.mysql import (
mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec, get_server_version
)
from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_native

Expand Down Expand Up @@ -338,7 +340,8 @@
'REPLICATION MASTER ADMIN',
'REPLICATION SLAVE',
'REPLICATION SLAVE ADMIN',
'SET USER',))
'SET USER',
'SHOW_ROUTINE',))


class InvalidPrivsError(Exception):
Expand Down Expand Up @@ -370,6 +373,19 @@ def use_old_user_mgmt(cursor):
return False


def supports_identified_by_password(cursor):
"""
Determines whether the 'CREATE USER %s@%s IDENTIFIED BY PASSWORD %s' syntax is supported. This was dropped in
MySQL 8.0.
"""
version_str = get_server_version(cursor)

if 'mariadb' in version_str.lower():
return True
else:
return LooseVersion(version_str) < LooseVersion('8')


def get_mode(cursor):
cursor.execute('SELECT @@GLOBAL.sql_mode')
result = cursor.fetchone()
Expand Down Expand Up @@ -475,7 +491,10 @@ def user_add(cursor, user, host, host_all, password, encrypted,
mogrify = do_not_mogrify_requires if old_user_mgmt else mogrify_requires

if password and encrypted:
query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password)
if supports_identified_by_password(cursor):
query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password)
else:
query_with_args = "CREATE USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, password)
elif password and not encrypted:
if old_user_mgmt:
query_with_args = "CREATE USER %s@%s IDENTIFIED BY %s", (user, host, password)
Expand Down
10 changes: 7 additions & 3 deletions tests/integration/targets/test_mysql_user/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,14 @@
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}

# ============================================================
# Update user password for a user.
# Assert the user password is updated and old password can no longer be used.
# Test plaintext and encrypted password scenarios.
#
#- include: user_password_update_test.yml
- include: test_user_password.yml

# ============================================================
# Test plugin authentication scenarios.
#
- include: test_user_plugin_auth.yml

# ============================================================
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database
Expand Down
269 changes: 269 additions & 0 deletions tests/integration/targets/test_mysql_user/tasks/test_user_password.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# Tests scenarios for both plaintext and encrypted user passwords.

- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
test_user_name: 'test_user_password'
initial_password: 'a5C8SN*DBa0%a75sGz'
initial_password_encrypted: '*0A12D4DF68C2A50716111674E565CA3D7D68B048'
new_password: 'NkN&qECv33vuQzf3bJg'
new_password_encrypted: '*B6559186FAD0953589F54383AD8EE9E9172296DA'
test_default_priv_type: 'SELECT'
test_default_priv: '*.*:{{ test_default_priv_type }}'

block:

# ============================================================
# Test setting plaintext password and changing it.
#

- name: Create user with initial password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password }}'
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that a change occurred because the user was added
assert:
that:
- "result.changed == true"

- include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}

- name: Get the MySQL version using the newly created used creds
mysql_info:
login_user: '{{ test_user_name }}'
login_password: '{{ initial_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that mysql_info was successful
assert:
that:
- "result.failed == false"

- name: Run mysql_user again without any changes
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password }}'
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that there weren't any changes because username/password didn't change
assert:
that:
- "result.changed == false"

- include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}

- name: Update the user password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ new_password }}'
state: present
register: result

- name: Assert that a change occurred because the password was updated
assert:
that:
- "result.changed == true"

- include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}

- name: Get the MySQL version data using the original password (should fail)
mysql_info:
login_user: '{{ test_user_name }}'
login_password: '{{ initial_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that the mysql_info module failed because we used the old password
assert:
that:
- "result.failed == true"

- name: Get the MySQL version data using the new password (should work)
mysql_info:
login_user: '{{ test_user_name }}'
login_password: '{{ new_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that the mysql_info module succeeded because we used the new password
assert:
that:
- "result.failed == false"

# Cleanup
- include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}

# ============================================================
# Test setting a plaintext password and then the same password encrypted to ensure there isn't a change detected.
#

- name: Create user with initial password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password }}'
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that a change occurred because the user was added
assert:
that:
- "result.changed == true"

- include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}

- name: Pass in the same password as before, but in the encrypted form (no change expected)
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password_encrypted }}'
encrypted: yes
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that there weren't any changes because username/password didn't change
assert:
that:
- "result.changed == false"

# Cleanup
- include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}

# ============================================================
# Test setting an encrypted password and then the same password in plaintext to ensure there isn't a change.
#

- name: Create user with initial password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password_encrypted }}'
encrypted: yes
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that a change occurred because the user was added
assert:
that:
- "result.changed == true"

- include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}

- name: Get the MySQL version data using the new creds
mysql_info:
login_user: '{{ test_user_name }}'
login_password: '{{ initial_password }}'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that the mysql_info module succeeded because we used the new password
assert:
that:
- "result.failed == false"

- name: Pass in the same password as before, but in the encrypted form (no change expected)
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
password: '{{ initial_password }}'
state: present
register: result

- name: Assert that there weren't any changes because username/password didn't change
assert:
that:
- "result.changed == false"

# Cleanup
- include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}

# ============================================================
# Test setting an empty password.
#

- name: Create user with empty password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that a change occurred because the user was added
assert:
that:
- "result.changed == true"

- name: Get the MySQL version using an empty password for the newly created user
mysql_info:
login_user: '{{ test_user_name }}'
login_password: ''
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that mysql_info was successful
assert:
that:
- "result.failed == false"

- name: Get the MySQL version using an non-empty password (should fail)
mysql_info:
login_user: '{{ test_user_name }}'
login_password: 'some_password'
login_host: '{{ mysql_host }}'
login_port: '{{ mysql_primary_port }}'
filter: version
register: result
ignore_errors: true

- name: Assert that mysql_info failed
assert:
that:
- "result.failed == true"

- name: Update the user without changing the password
mysql_user:
<<: *mysql_params
name: '{{ test_user_name }}'
priv: '{{ test_default_priv }}'
state: present
register: result

- name: Assert that the user wasn't changed because the password is still empty
assert:
that:
- "result.changed == false"

# Cleanup
- include: remove_user.yml user_name={{ test_user_name }} user_password=''
Loading

0 comments on commit 389eef0

Please sign in to comment.