Source code for saltext.mysql.states.mysql_user

"""
Management of MySQL users
=========================

:depends:   - MySQLdb Python module
:configuration: See :py:mod:`salt.modules.mysql` for setup instructions.

.. code-block:: yaml

    frank:
      mysql_user.present:
        - host: localhost
        - password: bobcat


.. versionadded:: 0.16.2
    Authentication overrides have been added.

The MySQL authentication information specified in the minion config file can be
overridden in states using the following arguments: ``connection_host``,
``connection_port``, ``connection_user``, ``connection_pass``,
``connection_db``, ``connection_unix_socket``, ``connection_default_file`` and
``connection_charset``.

.. code-block:: yaml

    frank:
      mysql_user.present:
        - host: localhost
        - password: "bob@cat"
        - connection_user: someuser
        - connection_pass: somepass
        - connection_charset: utf8
        - saltenv:
          - LC_ALL: "en_US.utf8"


This state is not able to grant permissions for the user. See
:py:mod:`salt.states.mysql_grants` for further instructions.

"""

import sys

import salt.utils.data


[docs] def __virtual__(): """ Only load if the mysql module is in __salt__ """ if "mysql.user_create" in __salt__: return True return (False, "mysql module could not be loaded")
def _get_mysql_error(): """ Look in module context for a MySQL error. Eventually we should make a less ugly way of doing this. """ return sys.modules[__salt__["test.ping"].__module__].__context__.pop("mysql.error", None)
[docs] def present( name, host="localhost", password=None, password_hash=None, allow_passwordless=False, unix_socket=False, password_column=None, auth_plugin="mysql_native_password", **connection_args, ): """ Ensure that the named user is present with the specified properties. A passwordless user can be configured by omitting ``password`` and ``password_hash``, and setting ``allow_passwordless`` to ``True``. name The name of the user to manage host Host for which this user/password combo applies password The password to use for this user. Will take precedence over the ``password_hash`` option if both are specified. password_hash The password in hashed form. Be sure to quote the password because YAML doesn't like the ``*``. A password hash can be obtained from the mysql command-line client like so:: mysql> SELECT PASSWORD('mypass'); +-------------------------------------------+ | PASSWORD('mypass') | +-------------------------------------------+ | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 | +-------------------------------------------+ 1 row in set (0.00 sec) allow_passwordless If ``True``, then ``password`` and ``password_hash`` can be omitted to permit a passwordless login. .. versionadded:: 0.16.2 unix_socket If ``True`` and allow_passwordless is ``True``, the unix_socket auth plugin will be used. """ ret = { "name": name, "changes": {}, "result": True, "comment": f"User {name}@{host} is already present", } passwordless = not any((password, password_hash)) # check if user exists with the same password (or passwordless login) if passwordless: if not salt.utils.data.is_true(allow_passwordless) and not unix_socket: ret["comment"] = ( "Either password or password_hash must be " "specified, unless allow_passwordless is True" ) ret["result"] = False return ret else: if __salt__["mysql.user_exists"]( name, host, passwordless=True, unix_socket=unix_socket, password_column=password_column, **connection_args, ): if allow_passwordless: ret["comment"] += " with passwordless login" return ret else: err = _get_mysql_error() if err is not None: ret["comment"] = err ret["result"] = False return ret else: if __salt__["mysql.user_exists"]( name, host, password, password_hash, unix_socket=unix_socket, password_column=password_column, **connection_args, ): if auth_plugin == "mysql_native_password": ret["comment"] += " with the desired password" if password_hash and not password: ret["comment"] += " hash" else: ret["comment"] += ". Unable to verify password." return ret else: err = _get_mysql_error() if err is not None: ret["comment"] = err ret["result"] = False return ret # check if user exists with a different password if __salt__["mysql.user_exists"](name, host, unix_socket=unix_socket, **connection_args): # The user is present, change the password if __opts__["test"]: ret["comment"] = f"Password for user {name}@{host} is set to be " ret["result"] = None if passwordless: ret["comment"] += "cleared" if not salt.utils.data.is_true(allow_passwordless): ret["comment"] += ", but allow_passwordless != True" ret["result"] = False else: ret["comment"] += "changed" return ret if __salt__["mysql.user_chpass"]( name, host, password, password_hash, allow_passwordless, unix_socket, **connection_args ): ret["comment"] = "Password for user {}@{} has been {}".format( name, host, "cleared" if passwordless else "changed" ) ret["changes"][name] = "Updated" else: ret["comment"] = "Failed to {} password for user {}@{}".format( "clear" if passwordless else "change", name, host ) err = _get_mysql_error() if err is not None: ret["comment"] += f" ({err})" if passwordless and not salt.utils.data.is_true(allow_passwordless): ret["comment"] += ( ". Note: allow_passwordless must be True " "to permit passwordless login." ) ret["result"] = False else: err = _get_mysql_error() if err is not None: ret["comment"] = err ret["result"] = False return ret # The user is not present, make it! if __opts__["test"]: ret["comment"] = f"User {name}@{host} is set to be added" ret["result"] = None if allow_passwordless: ret["comment"] += " with passwordless login" if not salt.utils.data.is_true(allow_passwordless): ret["comment"] += ", but allow_passwordless != True" ret["result"] = False if unix_socket: ret["comment"] += " using unix_socket" return ret if __salt__["mysql.user_create"]( name, host, password, password_hash, allow_passwordless, unix_socket=unix_socket, password_column=password_column, auth_plugin=auth_plugin, **connection_args, ): ret["comment"] = f"The user {name}@{host} has been added" if allow_passwordless: ret["comment"] += " with passwordless login" if unix_socket: ret["comment"] += " using unix_socket" ret["changes"][name] = "Present" else: ret["comment"] = f"Failed to create user {name}@{host}" err = _get_mysql_error() if err is not None: ret["comment"] += f" ({err})" ret["result"] = False return ret
[docs] def absent(name, host="localhost", **connection_args): """ Ensure that the named user is absent name The name of the user to remove """ ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Check if user exists, and if so, remove it if __salt__["mysql.user_exists"](name, host, **connection_args): if __opts__["test"]: ret["result"] = None ret["comment"] = f"User {name}@{host} is set to be removed" return ret if __salt__["mysql.user_remove"](name, host, **connection_args): ret["comment"] = f"User {name}@{host} has been removed" ret["changes"][name] = "Absent" return ret else: err = _get_mysql_error() if err is not None: ret["comment"] = err ret["result"] = False return ret else: err = _get_mysql_error() if err is not None: ret["comment"] = err ret["result"] = False return ret # fallback ret["comment"] = f"User {name}@{host} is not present, so it cannot be removed" return ret