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