| # -*- 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 |