blob: 49b66228a69536fc0ffe91b83b031ae1074ec907 [file] [log] [blame]
Roman Lubianyif8575832020-04-14 14:42:43 +03001# -*- coding: utf-8 -*-
2'''
3Manage RabbitMQ Users
4=====================
5
6Example:
7
8.. code-block:: yaml
9
10 rabbit_user:
11 rabbitmq_user.present:
12 - password: password
13 - force: True
14 - tags:
15 - monitoring
16 - user
17 - perms:
18 - '/':
19 - '.*'
20 - '.*'
21 - '.*'
22 - runas: rabbitmq
23'''
24
25# This file is copy of original /usr/lib/python2.7/dist-packages/salt/states/rabbitmq_user.py
26# with fix applied for PROD-35125 - rabbitmq module incompatible with rabbitmq-server 3.8+
27# It change module for check_password function.
28# Now it uses rabbitmq_custom module instead of rabbitmq for check_password function.
29
30# Import python libs
31from __future__ import absolute_import
32import logging
33
34# Import salt libs
35import salt.utils
36import salt.ext.six as six
37from salt.exceptions import CommandExecutionError
38
39log = logging.getLogger(__name__)
40
41
42def __virtual__():
43 '''
44 Only load if RabbitMQ is installed.
45 '''
46 return salt.utils.which('rabbitmqctl') is not None
47
48
49def _check_perms_changes(name, newperms, runas=None, existing=None):
50 '''
51 Check whether Rabbitmq user's permissions need to be changed.
52 '''
53 if not newperms:
54 return False
55
56 if existing is None:
57 try:
58 existing = __salt__['rabbitmq.list_user_permissions'](name, runas=runas)
59 except CommandExecutionError as err:
60 log.error('Error: {0}'.format(err))
61 return False
62
63 perm_need_change = False
64 for vhost_perms in newperms:
65 for vhost, perms in six.iteritems(vhost_perms):
66 if vhost in existing:
67 existing_vhost = existing[vhost]
68 if perms != existing_vhost:
69 # This checks for setting permissions to nothing in the state,
70 # when previous state runs have already set permissions to
71 # nothing. We don't want to report a change in this case.
72 if existing_vhost == '' and perms == ['', '', '']:
73 continue
74 perm_need_change = True
75 else:
76 perm_need_change = True
77
78 return perm_need_change
79
80
81def _get_current_tags(name, runas=None):
82 '''
83 Whether Rabbitmq user's tags need to be changed
84 '''
85 try:
86 return list(__salt__['rabbitmq.list_users'](runas=runas)[name])
87 except CommandExecutionError as err:
88 log.error('Error: {0}'.format(err))
89 return []
90
91
92def present(name,
93 password=None,
94 force=False,
95 tags=None,
96 perms=(),
97 runas=None):
98 '''
99 Ensure the RabbitMQ user exists.
100
101 name
102 User name
103 password
104 User's password, if one needs to be set
105 force
106 If user exists, forcibly change the password
107 tags
108 Optional list of tags for the user
109 perms
110 A list of dicts with vhost keys and 3-tuple values
111 runas
112 Name of the user to run the command
113 '''
114 ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
115
116 try:
117 user = __salt__['rabbitmq.user_exists'](name, runas=runas)
118 except CommandExecutionError as err:
119 ret['comment'] = 'Error: {0}'.format(err)
120 return ret
121
122 passwd_reqs_update = False
123 if user and password is not None:
124 try:
125 # Fix for PROD-35125
126 # Use rabbitmq_custom module instead of rabbitmq for check_password function
127 if not __salt__['rabbitmq_custom.check_password'](name,
128 password, runas=runas):
129 passwd_reqs_update = True
130 log.debug('RabbitMQ user %s password update required', name)
131 except CommandExecutionError as err:
132 ret['comment'] = 'Error: {0}'.format(err)
133 return ret
134
135 if user and not any((force, perms, tags, passwd_reqs_update)):
136 log.debug(('RabbitMQ user \'%s\' exists, password is up to'
137 ' date and force is not set.'), name)
138 ret['comment'] = 'User \'{0}\' is already present.'.format(name)
139 ret['result'] = True
140 return ret
141
142 if not user:
143 ret['changes'].update({'user':
144 {'old': '',
145 'new': name}})
146 if __opts__['test']:
147 ret['result'] = None
148 ret['comment'] = 'User \'{0}\' is set to be created.'.format(name)
149 return ret
150
151 log.debug(
152 'RabbitMQ user \'{0}\' doesn\'t exist - Creating.'.format(name))
153 try:
154 __salt__['rabbitmq.add_user'](name, password, runas=runas)
155 except CommandExecutionError as err:
156 ret['comment'] = 'Error: {0}'.format(err)
157 return ret
158 else:
159 log.debug('RabbitMQ user \'{0}\' exists'.format(name))
160 if force or passwd_reqs_update:
161 if password is not None:
162 if not __opts__['test']:
163 try:
164 __salt__['rabbitmq.change_password'](name, password, runas=runas)
165 except CommandExecutionError as err:
166 ret['comment'] = 'Error: {0}'.format(err)
167 return ret
168 ret['changes'].update({'password':
169 {'old': '',
170 'new': 'Set password.'}})
171 else:
172 if not __opts__['test']:
173 log.debug('Password for {0} is not set - Clearing password.'.format(name))
174 try:
175 __salt__['rabbitmq.clear_password'](name, runas=runas)
176 except CommandExecutionError as err:
177 ret['comment'] = 'Error: {0}'.format(err)
178 return ret
179 ret['changes'].update({'password':
180 {'old': 'Removed password.',
181 'new': ''}})
182
183 if tags is not None:
184 current_tags = _get_current_tags(name, runas=runas)
185 if isinstance(tags, str):
186 tags = tags.split()
187 # Diff the tags sets. Symmetric difference operator ^ will give us
188 # any element in one set, but not both
189 if set(tags) ^ set(current_tags):
190 if not __opts__['test']:
191 try:
192 __salt__['rabbitmq.set_user_tags'](name, tags, runas=runas)
193 except CommandExecutionError as err:
194 ret['comment'] = 'Error: {0}'.format(err)
195 return ret
196 ret['changes'].update({'tags':
197 {'old': current_tags,
198 'new': tags}})
199 try:
200 existing_perms = __salt__['rabbitmq.list_user_permissions'](name, runas=runas)
201 except CommandExecutionError as err:
202 ret['comment'] = 'Error: {0}'.format(err)
203 return ret
204
205 if _check_perms_changes(name, perms, runas=runas, existing=existing_perms):
206 for vhost_perm in perms:
207 for vhost, perm in six.iteritems(vhost_perm):
208 if not __opts__['test']:
209 try:
210 __salt__['rabbitmq.set_permissions'](
211 vhost, name, perm[0], perm[1], perm[2], runas=runas
212 )
213 except CommandExecutionError as err:
214 ret['comment'] = 'Error: {0}'.format(err)
215 return ret
216 new_perms = {vhost: perm}
217 if existing_perms != new_perms:
218 if ret['changes'].get('perms') is None:
219 ret['changes'].update({'perms':
220 {'old': {},
221 'new': {}}})
222 ret['changes']['perms']['old'].update(existing_perms)
223 ret['changes']['perms']['new'].update(new_perms)
224
225 ret['result'] = True
226 if ret['changes'] == {}:
227 ret['comment'] = '\'{0}\' is already in the desired state.'.format(name)
228 return ret
229
230 if __opts__['test']:
231 ret['result'] = None
232 ret['comment'] = 'Configuration for \'{0}\' will change.'.format(name)
233 return ret
234
235 ret['comment'] = '\'{0}\' was configured.'.format(name)
236 return ret