Skip to content

mysql_user: Granting all privileges at a global level always results in a change in MySQL 8.0 #77

@steveteahan

Description

@steveteahan
SUMMARY

Granting ALL PRIVILEGES at a global level will always result in a change being detected. I came across this issue when trying to re-enable the user_password_update_test.yml test for another change. This appears to be linked to the following change in MySQL 8.0:

In MySQL 8.0 compared to previous series, SHOW GRANTS no longer displays ALL PRIVILEGES in its global-privileges output because the meaning of ALL PRIVILEGES at the global level varies depending on which dynamic privileges are defined. Instead, SHOW GRANTS explictly lists each granted global privilege
(https://dev.mysql.com/doc/refman/8.0/en/show-grants.html)

So, because the expanded list of all privileges doesn't match "ALL PRIVILEGES", a change will always be made when applying *.*:ALL. Privileges at the database and/or table level are unaffected (hopefully the more common use case).

This issue appears to be complicated by the fact that dynamic privileges are defined at runtime as per the docs. This isn't my area of expertise, but my understanding of the implication of this is that we cannot reliably hard-code dynamic privileges to check. SHOW PRIVILEGES seems to get us the list of privileges that would be applied to a user with *.*:ALL (in addition to GRANT, PROXY, and USAGE).

I can think of a couple of options:

  1. This is the desired behavior. It can be added to the documentation and the test below can be added so the intent is clear. Possibly a warning?
  2. Build the list of privileges to check against using SHOW PRIVILEGES when priv is *.*:ALL

Like I mentioned, this isn't my area of expertise, so it is possible that hard-coding the privileges will really work, but purely based on the documentation it seems like it wouldn't.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

mysql_user

ANSIBLE VERSION

Development

CONFIGURATION

Development environment

OS / ENVIRONMENT

Development environment

STEPS TO REPRODUCE

The following test will fail and reproduce the issue:

- vars:
    mysql_parameters: &mysql_params
      login_user: '{{ mysql_user }}'
      login_password: '{{ mysql_password }}'
      login_host: 127.0.0.1
      login_port: '{{ mysql_primary_port }}'

  block:

    - name: Create a user with all privileges
      mysql_user:
        <<: *mysql_params
        name: '{{ user_name_5 }}'
        password: '{{ user_password_5 }}'
        priv: '*.*:ALL'
        state: present
      register: result

    - name: Assert that there was a change because the permissions did not exist previously
      assert:
        that:
          - "result.changed == true"

    - name: Test idempotency when nothing has changed and we're still granting ALL privileges
      mysql_user:
        <<: *mysql_params
        name: '{{ user_name_5 }}'
        password: '{{ user_password_5 }}'
        priv: '*.*:ALL'
        state: present
      register: result

    - name: Assert that there wasn't a change because the user already had ALL privileges
      assert:
        that:
          - "result.changed == false"

    ##########
    # Clean up

    - name: Drop test user
      mysql_user:
        <<: *mysql_params
        name: '{{ user_name_5 }}'
        state: absent
EXPECTED RESULTS

The test above should pass because no change was made to the privileges.

ACTUAL RESULTS
TASK [test_mysql_user : Test idempotency when nothing has changed and we're still granting ALL privileges] ***
task path: /root/ansible_collections/community/mysql/tests/output/.tmp/integration/test_mysql_user-sw27zu6h-ÅÑŚÌβŁÈ/tests/integration/targets/test_mysql_user/tasks/test_grant_all_privileges.yml:33
<testhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<testhost> EXEC /bin/sh -c 'echo ~root && sleep 0'
<testhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436 `" && echo ansible-tmp-1609109000.0398564-8365-196033537627436="` echo /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436 `" ) && sleep 0'
Using module file /root/ansible_collections/community/mysql/plugins/modules/mysql_user.py
<testhost> PUT /root/.ansible/tmp/ansible-local-101glv7bsmi/tmpd7a9ul1t TO /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436/AnsiballZ_mysql_user.py
<testhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436/ /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436/AnsiballZ_mysql_user.py && sleep 0'
<testhost> EXEC /bin/sh -c '/tmp/python-ypi9raas-ansible/python /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436/AnsiballZ_mysql_user.py && sleep 0'
<testhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1609109000.0398564-8365-196033537627436/ > /dev/null 2>&1 && sleep 0'
changed: [testhost] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "append_privs": false,
            "ca_cert": null,
            "check_hostname": null,
            "check_implicit_admin": false,
            "client_cert": null,
            "client_key": null,
            "config_file": "/root/.my.cnf",
            "connect_timeout": 30,
            "encrypted": false,
            "host": "localhost",
            "host_all": false,
            "login_host": "127.0.0.1",
            "login_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "login_port": 3307,
            "login_unix_socket": null,
            "login_user": "root",
            "name": "db_user5",
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "plugin": null,
            "plugin_auth_string": null,
            "plugin_hash_string": null,
            "priv": "*.*:ALL",
            "resource_limits": null,
            "sql_log_bin": true,
            "state": "present",
            "tls_requires": null,
            "update_password": "always",
            "user": "db_user5"
        }
    },
    "msg": "Privileges updated",
    "user": "db_user5"
}

TASK [test_mysql_user : Assert that there wasn't a change because the user already had ALL privileges] ***
task path: /root/ansible_collections/community/mysql/tests/output/.tmp/integration/test_mysql_user-sw27zu6h-ÅÑŚÌβŁÈ/tests/integration/targets/test_mysql_user/tasks/test_grant_all_privileges.yml:42
fatal: [testhost]: FAILED! => {
    "assertion": "result.changed == false",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions