Mercurial > libervia-backend
comparison sat/plugins/plugin_misc_email_invitation.py @ 4037:524856bd7b19
massive refactoring to switch from camelCase to snake_case:
historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a
pre-PEP8 code, to use the same coding style as in Twisted.
However, snake_case is more readable and it's better to follow PEP8 best practices, so it
has been decided to move on full snake_case. Because Libervia has a huge codebase, this
ended with a ugly mix of camelCase and snake_case.
To fix that, this patch does a big refactoring by renaming every function and method
(including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case.
This is a massive change, and may result in some bugs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:54:42 +0200 |
parents | cfc06915de15 |
children |
comparison
equal
deleted
inserted
replaced
4036:c4464d7ae97b | 4037:524856bd7b19 |
---|---|
75 | 75 |
76 def __init__(self, host): | 76 def __init__(self, host): |
77 log.info(_("plugin Invitations initialization")) | 77 log.info(_("plugin Invitations initialization")) |
78 self.host = host | 78 self.host = host |
79 self.invitations = persistent.LazyPersistentBinaryDict('invitations') | 79 self.invitations = persistent.LazyPersistentBinaryDict('invitations') |
80 host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s', | 80 host.bridge.add_method("invitation_create", ".plugin", in_sign='sasssssssssa{ss}s', |
81 out_sign='a{ss}', | 81 out_sign='a{ss}', |
82 method=self._create, | 82 method=self._create, |
83 async_=True) | 83 async_=True) |
84 host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}', | 84 host.bridge.add_method("invitation_get", ".plugin", in_sign='s', out_sign='a{ss}', |
85 method=self.get, | 85 method=self.get, |
86 async_=True) | 86 async_=True) |
87 host.bridge.addMethod("invitationDelete", ".plugin", in_sign='s', out_sign='', | 87 host.bridge.add_method("invitation_delete", ".plugin", in_sign='s', out_sign='', |
88 method=self._delete, | 88 method=self._delete, |
89 async_=True) | 89 async_=True) |
90 host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b', | 90 host.bridge.add_method("invitation_modify", ".plugin", in_sign='sa{ss}b', |
91 out_sign='', | 91 out_sign='', |
92 method=self._modify, | 92 method=self._modify, |
93 async_=True) | 93 async_=True) |
94 host.bridge.addMethod("invitationList", ".plugin", in_sign='s', | 94 host.bridge.add_method("invitation_list", ".plugin", in_sign='s', |
95 out_sign='a{sa{ss}}', | 95 out_sign='a{sa{ss}}', |
96 method=self._list, | 96 method=self._list, |
97 async_=True) | 97 async_=True) |
98 host.bridge.addMethod("invitationSimpleCreate", ".plugin", in_sign='sssss', | 98 host.bridge.add_method("invitation_simple_create", ".plugin", in_sign='sssss', |
99 out_sign='a{ss}', | 99 out_sign='a{ss}', |
100 method=self._simpleCreate, | 100 method=self._simple_create, |
101 async_=True) | 101 async_=True) |
102 | 102 |
103 def checkExtra(self, extra): | 103 def check_extra(self, extra): |
104 if EXTRA_RESERVED.intersection(extra): | 104 if EXTRA_RESERVED.intersection(extra): |
105 raise ValueError( | 105 raise ValueError( |
106 _("You can't use following key(s) in extra, they are reserved: {}") | 106 _("You can't use following key(s) in extra, they are reserved: {}") |
107 .format(', '.join(EXTRA_RESERVED.intersection(extra)))) | 107 .format(', '.join(EXTRA_RESERVED.intersection(extra)))) |
108 | 108 |
130 value = locals()[key] | 130 value = locals()[key] |
131 if value: | 131 if value: |
132 kwargs[key] = str(value) | 132 kwargs[key] = str(value) |
133 return defer.ensureDeferred(self.create(**kwargs)) | 133 return defer.ensureDeferred(self.create(**kwargs)) |
134 | 134 |
135 async def getExistingInvitation(self, email: Optional[str]) -> Optional[dict]: | 135 async def get_existing_invitation(self, email: Optional[str]) -> Optional[dict]: |
136 """Retrieve existing invitation with given email | 136 """Retrieve existing invitation with given email |
137 | 137 |
138 @param email: check if any invitation exist with this email | 138 @param email: check if any invitation exist with this email |
139 @return: first found invitation, or None if nothing found | 139 @return: first found invitation, or None if nothing found |
140 """ | 140 """ |
149 for id_, invitation in all_invitations.items(): | 149 for id_, invitation in all_invitations.items(): |
150 if invitation.get("email") == email: | 150 if invitation.get("email") == email: |
151 invitation[KEY_ID] = id_ | 151 invitation[KEY_ID] = id_ |
152 return invitation | 152 return invitation |
153 | 153 |
154 async def _createAccountAndProfile( | 154 async def _create_account_and_profile( |
155 self, | 155 self, |
156 id_: str, | 156 id_: str, |
157 kwargs: dict, | 157 kwargs: dict, |
158 extra: dict | 158 extra: dict |
159 ) -> None: | 159 ) -> None: |
160 """Create XMPP account and Libervia profile for guest""" | 160 """Create XMPP account and Libervia profile for guest""" |
161 ## XMPP account creation | 161 ## XMPP account creation |
162 password = kwargs.pop('password', None) | 162 password = kwargs.pop('password', None) |
163 if password is None: | 163 if password is None: |
164 password = utils.generatePassword() | 164 password = utils.generate_password() |
165 assert password | 165 assert password |
166 # XXX: password is here saved in clear in database | 166 # XXX: password is here saved in clear in database |
167 # it is needed for invitation as the same password is used for profile | 167 # it is needed for invitation as the same password is used for profile |
168 # and SàT need to be able to automatically open the profile with the uuid | 168 # and SàT need to be able to automatically open the profile with the uuid |
169 # FIXME: we could add an extra encryption key which would be used with the | 169 # FIXME: we could add an extra encryption key which would be used with the |
171 # not be saved and could be used to encrypt profile password. | 171 # not be saved and could be used to encrypt profile password. |
172 extra[KEY_PASSWORD] = password | 172 extra[KEY_PASSWORD] = password |
173 | 173 |
174 jid_ = kwargs.pop('jid_', None) | 174 jid_ = kwargs.pop('jid_', None) |
175 if not jid_: | 175 if not jid_: |
176 domain = self.host.memory.getConfig(None, 'xmpp_domain') | 176 domain = self.host.memory.config_get(None, 'xmpp_domain') |
177 if not domain: | 177 if not domain: |
178 # TODO: fallback to profile's domain | 178 # TODO: fallback to profile's domain |
179 raise ValueError(_("You need to specify xmpp_domain in sat.conf")) | 179 raise ValueError(_("You need to specify xmpp_domain in sat.conf")) |
180 jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), | 180 jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), |
181 domain=domain) | 181 domain=domain) |
184 | 184 |
185 if jid_.user: | 185 if jid_.user: |
186 # we don't register account if there is no user as anonymous login is then | 186 # we don't register account if there is no user as anonymous login is then |
187 # used | 187 # used |
188 try: | 188 try: |
189 await self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) | 189 await self.host.plugins['XEP-0077'].register_new_account(jid_, password) |
190 except error.StanzaError as e: | 190 except error.StanzaError as e: |
191 prefix = jid_.user | 191 prefix = jid_.user |
192 idx = 0 | 192 idx = 0 |
193 while e.condition == 'conflict': | 193 while e.condition == 'conflict': |
194 if idx >= SUFFIX_MAX: | 194 if idx >= SUFFIX_MAX: |
195 raise exceptions.ConflictError(_("Can't create XMPP account")) | 195 raise exceptions.ConflictError(_("Can't create XMPP account")) |
196 jid_.user = prefix + '_' + str(idx) | 196 jid_.user = prefix + '_' + str(idx) |
197 log.info(_("requested jid already exists, trying with {}".format( | 197 log.info(_("requested jid already exists, trying with {}".format( |
198 jid_.full()))) | 198 jid_.full()))) |
199 try: | 199 try: |
200 await self.host.plugins['XEP-0077'].registerNewAccount( | 200 await self.host.plugins['XEP-0077'].register_new_account( |
201 jid_, | 201 jid_, |
202 password | 202 password |
203 ) | 203 ) |
204 except error.StanzaError: | 204 except error.StanzaError: |
205 idx += 1 | 205 idx += 1 |
214 | 214 |
215 extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format( | 215 extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format( |
216 uuid=id_ | 216 uuid=id_ |
217 ) | 217 ) |
218 # profile creation should not fail as we generate unique name ourselves | 218 # profile creation should not fail as we generate unique name ourselves |
219 await self.host.memory.createProfile(guest_profile, password) | 219 await self.host.memory.create_profile(guest_profile, password) |
220 await self.host.memory.startSession(password, guest_profile) | 220 await self.host.memory.start_session(password, guest_profile) |
221 await self.host.memory.setParam("JabberID", jid_.full(), "Connection", | 221 await self.host.memory.param_set("JabberID", jid_.full(), "Connection", |
222 profile_key=guest_profile) | 222 profile_key=guest_profile) |
223 await self.host.memory.setParam("Password", password, "Connection", | 223 await self.host.memory.param_set("Password", password, "Connection", |
224 profile_key=guest_profile) | 224 profile_key=guest_profile) |
225 | 225 |
226 async def create(self, **kwargs): | 226 async def create(self, **kwargs): |
227 r"""Create an invitation | 227 r"""Create an invitation |
228 | 228 |
288 if set(kwargs).intersection(extra): | 288 if set(kwargs).intersection(extra): |
289 raise ValueError( | 289 raise ValueError( |
290 _("You can't use following key(s) in both args and extra: {}").format( | 290 _("You can't use following key(s) in both args and extra: {}").format( |
291 ', '.join(set(kwargs).intersection(extra)))) | 291 ', '.join(set(kwargs).intersection(extra)))) |
292 | 292 |
293 self.checkExtra(extra) | 293 self.check_extra(extra) |
294 | 294 |
295 email = kwargs.pop('email', None) | 295 email = kwargs.pop('email', None) |
296 | 296 |
297 existing = await self.getExistingInvitation(email) | 297 existing = await self.get_existing_invitation(email) |
298 if existing is not None: | 298 if existing is not None: |
299 log.info(f"There is already an invitation for {email!r}") | 299 log.info(f"There is already an invitation for {email!r}") |
300 extra.update(existing) | 300 extra.update(existing) |
301 del extra[KEY_ID] | 301 del extra[KEY_ID] |
302 | 302 |
314 ## uuid | 314 ## uuid |
315 log.info(_("creating an invitation")) | 315 log.info(_("creating an invitation")) |
316 id_ = existing[KEY_ID] if existing else str(shortuuid.uuid()) | 316 id_ = existing[KEY_ID] if existing else str(shortuuid.uuid()) |
317 | 317 |
318 if existing is None: | 318 if existing is None: |
319 await self._createAccountAndProfile(id_, kwargs, extra) | 319 await self._create_account_and_profile(id_, kwargs, extra) |
320 | 320 |
321 profile = kwargs.pop('profile', None) | 321 profile = kwargs.pop('profile', None) |
322 guest_profile = extra[KEY_GUEST_PROFILE] | 322 guest_profile = extra[KEY_GUEST_PROFILE] |
323 jid_ = jid.JID(extra[KEY_JID]) | 323 jid_ = jid.JID(extra[KEY_JID]) |
324 | 324 |
331 id_plugin = self.host.plugins['IDENTITY'] | 331 id_plugin = self.host.plugins['IDENTITY'] |
332 except KeyError: | 332 except KeyError: |
333 pass | 333 pass |
334 else: | 334 else: |
335 await self.host.connect(guest_profile, password) | 335 await self.host.connect(guest_profile, password) |
336 guest_client = self.host.getClient(guest_profile) | 336 guest_client = self.host.get_client(guest_profile) |
337 await id_plugin.setIdentity(guest_client, {'nicknames': [name]}) | 337 await id_plugin.set_identity(guest_client, {'nicknames': [name]}) |
338 await self.host.disconnect(guest_profile) | 338 await self.host.disconnect(guest_profile) |
339 | 339 |
340 ## email | 340 ## email |
341 language = kwargs.pop('language', None) | 341 language = kwargs.pop('language', None) |
342 if language is not None: | 342 if language is not None: |
368 format_args['host_name'] = extra['host_name'] = host_name | 368 format_args['host_name'] = extra['host_name'] = host_name |
369 | 369 |
370 invite_url = url_template.format(**format_args) | 370 invite_url = url_template.format(**format_args) |
371 format_args['url'] = invite_url | 371 format_args['url'] = invite_url |
372 | 372 |
373 await sat_email.sendEmail( | 373 await sat_email.send_email( |
374 self.host.memory.config, | 374 self.host.memory.config, |
375 [email] + emails_extra, | 375 [email] + emails_extra, |
376 (kwargs.pop('message_subject', None) or DEFAULT_SUBJECT).format( | 376 (kwargs.pop('message_subject', None) or DEFAULT_SUBJECT).format( |
377 **format_args), | 377 **format_args), |
378 (kwargs.pop('message_body', None) or DEFAULT_BODY).format(**format_args), | 378 (kwargs.pop('message_body', None) or DEFAULT_BODY).format(**format_args), |
382 | 382 |
383 # we automatically add guest to host roster (if host is specified) | 383 # we automatically add guest to host roster (if host is specified) |
384 # FIXME: a parameter to disable auto roster adding would be nice | 384 # FIXME: a parameter to disable auto roster adding would be nice |
385 if profile is not None: | 385 if profile is not None: |
386 try: | 386 try: |
387 client = self.host.getClient(profile) | 387 client = self.host.get_client(profile) |
388 except Exception as e: | 388 except Exception as e: |
389 log.error(f"Can't get host profile: {profile}: {e}") | 389 log.error(f"Can't get host profile: {profile}: {e}") |
390 else: | 390 else: |
391 await self.host.updateContact(client, jid_, name, ['guests']) | 391 await self.host.contact_update(client, jid_, name, ['guests']) |
392 | 392 |
393 if kwargs: | 393 if kwargs: |
394 log.warning(_("Not all arguments have been consumed: {}").format(kwargs)) | 394 log.warning(_("Not all arguments have been consumed: {}").format(kwargs)) |
395 | 395 |
396 ## extra data saving | 396 ## extra data saving |
398 | 398 |
399 extra[KEY_ID] = id_ | 399 extra[KEY_ID] = id_ |
400 | 400 |
401 return extra | 401 return extra |
402 | 402 |
403 def _simpleCreate(self, invitee_email, invitee_name, url_template, extra_s, profile): | 403 def _simple_create(self, invitee_email, invitee_name, url_template, extra_s, profile): |
404 client = self.host.getClient(profile) | 404 client = self.host.get_client(profile) |
405 # FIXME: needed because python-dbus use a specific string class | 405 # FIXME: needed because python-dbus use a specific string class |
406 invitee_email = str(invitee_email) | 406 invitee_email = str(invitee_email) |
407 invitee_name = str(invitee_name) | 407 invitee_name = str(invitee_name) |
408 url_template = str(url_template) | 408 url_template = str(url_template) |
409 extra = data_format.deserialise(extra_s) | 409 extra = data_format.deserialise(extra_s) |
410 d = defer.ensureDeferred( | 410 d = defer.ensureDeferred( |
411 self.simpleCreate(client, invitee_email, invitee_name, url_template, extra) | 411 self.simple_create(client, invitee_email, invitee_name, url_template, extra) |
412 ) | 412 ) |
413 d.addCallback(lambda data: {k: str(v) for k,v in data.items()}) | 413 d.addCallback(lambda data: {k: str(v) for k,v in data.items()}) |
414 return d | 414 return d |
415 | 415 |
416 async def simpleCreate( | 416 async def simple_create( |
417 self, client, invitee_email, invitee_name, url_template, extra): | 417 self, client, invitee_email, invitee_name, url_template, extra): |
418 """Simplified method to invite somebody by email""" | 418 """Simplified method to invite somebody by email""" |
419 return await self.create( | 419 return await self.create( |
420 name=invitee_name, | 420 name=invitee_name, |
421 email=invitee_email, | 421 email=invitee_email, |
441 data = await self.get(id_) | 441 data = await self.get(id_) |
442 guest_profile = data['guest_profile'] | 442 guest_profile = data['guest_profile'] |
443 password = data['password'] | 443 password = data['password'] |
444 try: | 444 try: |
445 await self.host.connect(guest_profile, password) | 445 await self.host.connect(guest_profile, password) |
446 guest_client = self.host.getClient(guest_profile) | 446 guest_client = self.host.get_client(guest_profile) |
447 # XXX: be extra careful to use guest_client and not client below, as this will | 447 # XXX: be extra careful to use guest_client and not client below, as this will |
448 # delete the associated XMPP account | 448 # delete the associated XMPP account |
449 log.debug("deleting XMPP account") | 449 log.debug("deleting XMPP account") |
450 await self.host.plugins['XEP-0077'].unregister(guest_client, None) | 450 await self.host.plugins['XEP-0077'].unregister(guest_client, None) |
451 except (error.StanzaError, sasl.SASLAuthError) as e: | 451 except (error.StanzaError, sasl.SASLAuthError) as e: |
452 log.warning( | 452 log.warning( |
453 f"Can't delete {guest_profile}'s XMPP account, maybe it as already been " | 453 f"Can't delete {guest_profile}'s XMPP account, maybe it as already been " |
454 f"deleted: {e}") | 454 f"deleted: {e}") |
455 try: | 455 try: |
456 await self.host.memory.asyncDeleteProfile(guest_profile, True) | 456 await self.host.memory.profile_delete_async(guest_profile, True) |
457 except Exception as e: | 457 except Exception as e: |
458 log.warning(f"Can't delete guest profile {guest_profile}: {e}") | 458 log.warning(f"Can't delete guest profile {guest_profile}: {e}") |
459 log.debug("removing guest data") | 459 log.debug("removing guest data") |
460 await self.invitations.adel(id_) | 460 await self.invitations.adel(id_) |
461 log.info(f"{id_} invitation has been deleted") | 461 log.info(f"{id_} invitation has been deleted") |
472 empty values will be deleted if replace is True | 472 empty values will be deleted if replace is True |
473 @param replace(bool): if True replace the data | 473 @param replace(bool): if True replace the data |
474 else update them | 474 else update them |
475 @raise KeyError: there is not invitation with this id_ | 475 @raise KeyError: there is not invitation with this id_ |
476 """ | 476 """ |
477 self.checkExtra(new_extra) | 477 self.check_extra(new_extra) |
478 def gotCurrentData(current_data): | 478 def got_current_data(current_data): |
479 if replace: | 479 if replace: |
480 new_data = new_extra | 480 new_data = new_extra |
481 for k in EXTRA_RESERVED: | 481 for k in EXTRA_RESERVED: |
482 try: | 482 try: |
483 new_data[k] = current_data[k] | 483 new_data[k] = current_data[k] |
498 pass | 498 pass |
499 | 499 |
500 self.invitations[id_] = new_data | 500 self.invitations[id_] = new_data |
501 | 501 |
502 d = self.invitations[id_] | 502 d = self.invitations[id_] |
503 d.addCallback(gotCurrentData) | 503 d.addCallback(got_current_data) |
504 return d | 504 return d |
505 | 505 |
506 def _list(self, profile=C.PROF_KEY_NONE): | 506 def _list(self, profile=C.PROF_KEY_NONE): |
507 return defer.ensureDeferred(self.list(profile)) | 507 return defer.ensureDeferred(self.list(profile)) |
508 | 508 |