Roman Lubianyi | f857583 | 2020-04-14 14:42:43 +0300 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | # This file is copy of original /usr/lib/python2.7/dist-packages/salt/modules/rabbitmq.py |
| 4 | # with fix applied for PROD-35125 - rabbitmq module incompatible with rabbitmq-server 3.8+ |
| 5 | # It fix native check_password function by override it here and call it from |
| 6 | # _states/rabbitmq_user_common.py which also introduced in this patch. |
| 7 | # Fix source: https://github.com/saltstack/salt/commit/d0e0ed6b60f4de6d2b2551cad2fc7a553efe0274 |
| 8 | |
| 9 | from __future__ import absolute_import |
| 10 | |
| 11 | # Import python libs |
| 12 | import json |
| 13 | import re |
| 14 | import logging |
| 15 | import os |
| 16 | import os.path |
| 17 | import random |
| 18 | import string |
| 19 | |
| 20 | # Import salt libs |
| 21 | import salt.utils |
| 22 | import salt.utils.itertools |
| 23 | import salt.ext.six as six |
| 24 | from salt.exceptions import SaltInvocationError |
| 25 | from salt.ext.six.moves import range |
| 26 | from salt.exceptions import CommandExecutionError |
| 27 | |
| 28 | log = logging.getLogger(__name__) |
| 29 | |
| 30 | RABBITMQCTL = None |
| 31 | RABBITMQ_PLUGINS = None |
| 32 | |
| 33 | |
| 34 | def __virtual__(): |
| 35 | ''' |
| 36 | Verify RabbitMQ is installed. |
| 37 | ''' |
| 38 | global RABBITMQCTL |
| 39 | global RABBITMQ_PLUGINS |
| 40 | |
| 41 | if salt.utils.is_windows(): |
| 42 | from salt.ext.six.moves import winreg |
| 43 | key = None |
| 44 | try: |
| 45 | key = winreg.OpenKeyEx( |
| 46 | winreg.HKEY_LOCAL_MACHINE, |
| 47 | 'SOFTWARE\\VMware, Inc.\\RabbitMQ Server', |
| 48 | 0, |
| 49 | winreg.KEY_READ | winreg.KEY_WOW64_32KEY |
| 50 | ) |
| 51 | (dir_path, value_type) = winreg.QueryValueEx( |
| 52 | key, |
| 53 | 'Install_Dir' |
| 54 | ) |
| 55 | if value_type != winreg.REG_SZ: |
| 56 | raise TypeError('Invalid RabbitMQ Server directory type: {0}'.format(value_type)) |
| 57 | if not os.path.isdir(dir_path): |
| 58 | raise IOError('RabbitMQ directory not found: {0}'.format(dir_path)) |
| 59 | subdir_match = '' |
| 60 | for name in os.listdir(dir_path): |
| 61 | if name.startswith('rabbitmq_server-'): |
| 62 | subdir_path = os.path.join(dir_path, name) |
| 63 | # Get the matching entry that is last in ASCII order. |
| 64 | if os.path.isdir(subdir_path) and subdir_path > subdir_match: |
| 65 | subdir_match = subdir_path |
| 66 | if not subdir_match: |
| 67 | raise IOError('"rabbitmq_server-*" subdirectory not found in: {0}'.format(dir_path)) |
| 68 | RABBITMQCTL = os.path.join(subdir_match, 'sbin', 'rabbitmqctl.bat') |
| 69 | RABBITMQ_PLUGINS = os.path.join(subdir_match, 'sbin', 'rabbitmq-plugins.bat') |
| 70 | except Exception: |
| 71 | pass |
| 72 | finally: |
| 73 | if key is not None: |
| 74 | winreg.CloseKey(key) |
| 75 | else: |
| 76 | RABBITMQCTL = salt.utils.which('rabbitmqctl') |
| 77 | RABBITMQ_PLUGINS = salt.utils.which('rabbitmq-plugins') |
| 78 | |
| 79 | if not RABBITMQCTL: |
| 80 | return (False, 'Module rabbitmq: module only works when RabbitMQ is installed') |
| 81 | return True |
| 82 | |
| 83 | |
| 84 | def _format_response(response, msg): |
| 85 | if isinstance(response, dict): |
| 86 | if response['retcode'] != 0 or response['stderr']: |
| 87 | raise CommandExecutionError( |
| 88 | 'RabbitMQ command failed: {0}'.format(response['stderr']) |
| 89 | ) |
| 90 | else: |
| 91 | response = response['stdout'] |
| 92 | else: |
| 93 | if 'Error' in response: |
| 94 | raise CommandExecutionError( |
| 95 | 'RabbitMQ command failed: {0}'.format(response) |
| 96 | ) |
| 97 | return { |
| 98 | msg: response |
| 99 | } |
| 100 | |
| 101 | |
| 102 | def check_password(name, password, runas=None): |
| 103 | ''' |
| 104 | .. versionadded:: 2016.3.0 |
| 105 | |
| 106 | Checks if a user's password is valid. |
| 107 | |
| 108 | CLI Example: |
| 109 | |
| 110 | .. code-block:: bash |
| 111 | |
| 112 | salt '*' rabbitmq.check_password rabbit_user password |
| 113 | ''' |
| 114 | # try to get the rabbitmq-version - adapted from _get_rabbitmq_plugin |
| 115 | |
| 116 | if runas is None and not salt.utils.is_windows(): |
| 117 | runas = salt.utils.get_user() |
| 118 | |
| 119 | try: |
| 120 | res = __salt__['cmd.run']([RABBITMQCTL, 'status'], reset_system_locale=False, runas=runas, python_shell=False) |
| 121 | |
| 122 | # Fix for PROD-35125 - rabbitmq module incompatible with rabbitmq-server 3.8+ |
| 123 | # https://github.com/saltstack/salt/commit/d0e0ed6b60f4de6d2b2551cad2fc7a553efe0274 |
| 124 | |
| 125 | # Check regex against older RabbitMQ version status output |
| 126 | old_server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res) |
| 127 | # Check regex against newer RabbitMQ version status output |
| 128 | server_version = re.search(r"RabbitMQ version:\s*(.+)", res) |
| 129 | |
| 130 | if server_version is None and old_server_version is None: |
| 131 | raise ValueError |
| 132 | |
| 133 | if old_server_version: |
| 134 | server_version = old_server_version |
| 135 | server_version = server_version.group(1).split("-")[0] |
| 136 | version = [int(i) for i in server_version.split(".")] |
| 137 | |
| 138 | except ValueError: |
| 139 | version = (0, 0, 0) |
| 140 | if len(version) < 3: |
| 141 | version = (0, 0, 0) |
| 142 | |
| 143 | # rabbitmq introduced a native api to check a username and password in version 3.5.7. |
| 144 | if tuple(version) >= (3, 5, 7): |
| 145 | if salt.utils.is_windows(): |
| 146 | # On Windows, if the password contains a special character |
| 147 | # such as '|', normal execution will fail. For example: |
| 148 | # cmd: rabbitmq.add_user abc "asdf|def" |
| 149 | # stderr: 'def' is not recognized as an internal or external |
| 150 | # command,\r\noperable program or batch file. |
| 151 | # Work around this by using a shell and a quoted command. |
| 152 | python_shell = True |
| 153 | cmd = '"{0}" authenticate_user "{1}" "{2}"'.format( |
| 154 | RABBITMQCTL, name, password |
| 155 | ) |
| 156 | else: |
| 157 | python_shell = False |
| 158 | cmd = [RABBITMQCTL, 'authenticate_user', name, password] |
| 159 | |
| 160 | res = __salt__['cmd.run_all']( |
| 161 | cmd, |
| 162 | reset_system_locale=False, |
| 163 | runas=runas, |
| 164 | output_loglevel='quiet', |
| 165 | python_shell=python_shell) |
| 166 | |
| 167 | if res['retcode'] != 0 or res['stderr']: |
| 168 | return False |
| 169 | return True |
| 170 | |
| 171 | cmd = ('rabbit_auth_backend_internal:check_user_login' |
| 172 | '(<<"{0}">>, [{{password, <<"{1}">>}}]).').format( |
| 173 | name.replace('"', '\\"'), |
| 174 | password.replace('"', '\\"')) |
| 175 | |
| 176 | res = __salt__['cmd.run_all']( |
| 177 | [RABBITMQCTL, 'eval', cmd], |
| 178 | reset_system_locale=False, |
| 179 | runas=runas, |
| 180 | output_loglevel='quiet', |
| 181 | python_shell=False) |
| 182 | msg = 'password-check' |
| 183 | |
| 184 | _response = _format_response(res, msg) |
| 185 | _key = _response.keys()[0] |
| 186 | |
| 187 | if 'invalid credentials' in _response[_key]: |
| 188 | return False |
| 189 | |
| 190 | return True |