blob: 182ca49729d9ea64622d707e4deebf2a9552832d [file] [log] [blame]
Roman Lubianyif8575832020-04-14 14:42:43 +03001# -*- 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
9from __future__ import absolute_import
10
11# Import python libs
12import json
13import re
14import logging
15import os
16import os.path
17import random
18import string
19
20# Import salt libs
21import salt.utils
22import salt.utils.itertools
23import salt.ext.six as six
24from salt.exceptions import SaltInvocationError
25from salt.ext.six.moves import range
26from salt.exceptions import CommandExecutionError
27
28log = logging.getLogger(__name__)
29
30RABBITMQCTL = None
31RABBITMQ_PLUGINS = None
32
33
34def __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
84def _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
102def 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