blob: 182ca49729d9ea64622d707e4deebf2a9552832d [file] [log] [blame]
# -*- coding: utf-8 -*-
# This file is copy of original /usr/lib/python2.7/dist-packages/salt/modules/rabbitmq.py
# with fix applied for PROD-35125 - rabbitmq module incompatible with rabbitmq-server 3.8+
# It fix native check_password function by override it here and call it from
# _states/rabbitmq_user_common.py which also introduced in this patch.
# Fix source: https://github.com/saltstack/salt/commit/d0e0ed6b60f4de6d2b2551cad2fc7a553efe0274
from __future__ import absolute_import
# Import python libs
import json
import re
import logging
import os
import os.path
import random
import string
# Import salt libs
import salt.utils
import salt.utils.itertools
import salt.ext.six as six
from salt.exceptions import SaltInvocationError
from salt.ext.six.moves import range
from salt.exceptions import CommandExecutionError
log = logging.getLogger(__name__)
RABBITMQCTL = None
RABBITMQ_PLUGINS = None
def __virtual__():
'''
Verify RabbitMQ is installed.
'''
global RABBITMQCTL
global RABBITMQ_PLUGINS
if salt.utils.is_windows():
from salt.ext.six.moves import winreg
key = None
try:
key = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
'SOFTWARE\\VMware, Inc.\\RabbitMQ Server',
0,
winreg.KEY_READ | winreg.KEY_WOW64_32KEY
)
(dir_path, value_type) = winreg.QueryValueEx(
key,
'Install_Dir'
)
if value_type != winreg.REG_SZ:
raise TypeError('Invalid RabbitMQ Server directory type: {0}'.format(value_type))
if not os.path.isdir(dir_path):
raise IOError('RabbitMQ directory not found: {0}'.format(dir_path))
subdir_match = ''
for name in os.listdir(dir_path):
if name.startswith('rabbitmq_server-'):
subdir_path = os.path.join(dir_path, name)
# Get the matching entry that is last in ASCII order.
if os.path.isdir(subdir_path) and subdir_path > subdir_match:
subdir_match = subdir_path
if not subdir_match:
raise IOError('"rabbitmq_server-*" subdirectory not found in: {0}'.format(dir_path))
RABBITMQCTL = os.path.join(subdir_match, 'sbin', 'rabbitmqctl.bat')
RABBITMQ_PLUGINS = os.path.join(subdir_match, 'sbin', 'rabbitmq-plugins.bat')
except Exception:
pass
finally:
if key is not None:
winreg.CloseKey(key)
else:
RABBITMQCTL = salt.utils.which('rabbitmqctl')
RABBITMQ_PLUGINS = salt.utils.which('rabbitmq-plugins')
if not RABBITMQCTL:
return (False, 'Module rabbitmq: module only works when RabbitMQ is installed')
return True
def _format_response(response, msg):
if isinstance(response, dict):
if response['retcode'] != 0 or response['stderr']:
raise CommandExecutionError(
'RabbitMQ command failed: {0}'.format(response['stderr'])
)
else:
response = response['stdout']
else:
if 'Error' in response:
raise CommandExecutionError(
'RabbitMQ command failed: {0}'.format(response)
)
return {
msg: response
}
def check_password(name, password, runas=None):
'''
.. versionadded:: 2016.3.0
Checks if a user's password is valid.
CLI Example:
.. code-block:: bash
salt '*' rabbitmq.check_password rabbit_user password
'''
# try to get the rabbitmq-version - adapted from _get_rabbitmq_plugin
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
try:
res = __salt__['cmd.run']([RABBITMQCTL, 'status'], reset_system_locale=False, runas=runas, python_shell=False)
# Fix for PROD-35125 - rabbitmq module incompatible with rabbitmq-server 3.8+
# https://github.com/saltstack/salt/commit/d0e0ed6b60f4de6d2b2551cad2fc7a553efe0274
# Check regex against older RabbitMQ version status output
old_server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res)
# Check regex against newer RabbitMQ version status output
server_version = re.search(r"RabbitMQ version:\s*(.+)", res)
if server_version is None and old_server_version is None:
raise ValueError
if old_server_version:
server_version = old_server_version
server_version = server_version.group(1).split("-")[0]
version = [int(i) for i in server_version.split(".")]
except ValueError:
version = (0, 0, 0)
if len(version) < 3:
version = (0, 0, 0)
# rabbitmq introduced a native api to check a username and password in version 3.5.7.
if tuple(version) >= (3, 5, 7):
if salt.utils.is_windows():
# On Windows, if the password contains a special character
# such as '|', normal execution will fail. For example:
# cmd: rabbitmq.add_user abc "asdf|def"
# stderr: 'def' is not recognized as an internal or external
# command,\r\noperable program or batch file.
# Work around this by using a shell and a quoted command.
python_shell = True
cmd = '"{0}" authenticate_user "{1}" "{2}"'.format(
RABBITMQCTL, name, password
)
else:
python_shell = False
cmd = [RABBITMQCTL, 'authenticate_user', name, password]
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=python_shell)
if res['retcode'] != 0 or res['stderr']:
return False
return True
cmd = ('rabbit_auth_backend_internal:check_user_login'
'(<<"{0}">>, [{{password, <<"{1}">>}}]).').format(
name.replace('"', '\\"'),
password.replace('"', '\\"'))
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'eval', cmd],
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=False)
msg = 'password-check'
_response = _format_response(res, msg)
_key = _response.keys()[0]
if 'invalid credentials' in _response[_key]:
return False
return True