Mercurial > libervia-backend
diff sat/plugins/plugin_misc_invitations.py @ 2909:90146552cde5
core (memory), plugin XEP-0329, plugin invitation: minor style improvments
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 14 Apr 2019 08:21:51 +0200 |
parents | 003b8b4b56a7 |
children |
line wrap: on
line diff
--- a/sat/plugins/plugin_misc_invitations.py Sun Apr 14 08:21:51 2019 +0200 +++ b/sat/plugins/plugin_misc_invitations.py Sun Apr 14 08:21:51 2019 +0200 @@ -17,20 +17,21 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import shortuuid +from twisted.internet import defer +from twisted.words.protocols.jabber import jid +from twisted.words.protocols.jabber import error from sat.core.i18n import _, D_ from sat.core.constants import Const as C from sat.core import exceptions from sat.core.log import getLogger -log = getLogger(__name__) -import shortuuid from sat.tools import utils from sat.tools.common import data_format -from twisted.internet import defer -from twisted.words.protocols.jabber import jid -from twisted.words.protocols.jabber import error from sat.memory import persistent from sat.tools import email as sat_email +log = getLogger(__name__) + PLUGIN_INFO = { C.PI_NAME: "Invitations", @@ -53,7 +54,8 @@ KEY_GUEST_PROFILE = u'guest_profile' KEY_PASSWORD = u'password' KEY_EMAILS_EXTRA = u'emails_extra' -EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION, KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA} +EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION, + KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA} DEFAULT_SUBJECT = D_(u"You have been invited by {host_name} to {app_name}") DEFAULT_BODY = D_(u"""Hello {name}! @@ -74,26 +76,33 @@ log.info(_(u"plugin Invitations initialization")) self.host = host self.invitations = persistent.LazyPersistentBinaryDict(u'invitations') - host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s', out_sign='a{ss}', + host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s', + out_sign='a{ss}', method=self._create, async=True) host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}', method=self.get, async=True) - host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b', out_sign='', + host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b', + out_sign='', method=self._modify, async=True) - host.bridge.addMethod("invitationList", ".plugin", in_sign='s', out_sign='a{sa{ss}}', + host.bridge.addMethod("invitationList", ".plugin", in_sign='s', + out_sign='a{sa{ss}}', method=self._list, async=True) def checkExtra(self, extra): if EXTRA_RESERVED.intersection(extra): - raise ValueError(_(u"You can't use following key(s) in extra, they are reserved: {}").format( - u', '.join(EXTRA_RESERVED.intersection(extra)))) + raise ValueError( + _(u"You can't use following key(s) in extra, they are reserved: {}") + .format(u', '.join(EXTRA_RESERVED.intersection(extra)))) - def _create(self, email=u'', emails_extra=None, jid_=u'', password=u'', name=u'', host_name=u'', language=u'', url_template=u'', message_subject=u'', message_body=u'', extra=None, profile=u''): - # XXX: we don't use **kwargs here to keep arguments name for introspection with D-Bus bridge + def _create(self, email=u'', emails_extra=None, jid_=u'', password=u'', name=u'', + host_name=u'', language=u'', url_template=u'', message_subject=u'', + message_body=u'', extra=None, profile=u''): + # XXX: we don't use **kwargs here to keep arguments name for introspection with + # D-Bus bridge if emails_extra is None: emails_extra = [] @@ -106,8 +115,10 @@ KEY_EMAILS_EXTRA: [unicode(e) for e in emails_extra] } - # we need to be sure that values are unicode, else they won't be pickled correctly with D-Bus - for key in ("jid_", "password", "name", "host_name", "email", "language", "url_template", "message_subject", "message_body", "profile"): + # we need to be sure that values are unicode, else they won't be pickled correctly + # with D-Bus + for key in ("jid_", "password", "name", "host_name", "email", "language", + "url_template", "message_subject", "message_body", "profile"): value = locals()[key] if value: kwargs[key] = unicode(value) @@ -120,32 +131,45 @@ @defer.inlineCallbacks def create(self, **kwargs): - ur"""create an invitation + ur"""Create an invitation - this will create an XMPP account and a profile, and use a UUID to retrieve them. - the profile is automatically generated in the form guest@@[UUID], this way they can be retrieved easily - **kwargs: keywords arguments which can have the following keys, unset values are equivalent to None: - jid_(jid.JID, None): jid to use for invitation, the jid will be created using XEP-0077 - if the jid has no user part, an anonymous account will be used (no XMPP account created in this case) - if None, automatically generate an account name (in the form "invitation-[random UUID]@domain.tld") (note that this UUID is not the - same as the invitation one, as jid can be used publicly (leaking the UUID), and invitation UUID give access to account. - in case of conflict, a suffix number is added to the account until a free one if found (with a failure if SUFFIX_MAX is reached) - password(unicode, None): password to use (will be used for XMPP account and profile) + This will create an XMPP account and a profile, and use a UUID to retrieve them. + The profile is automatically generated in the form guest@@[UUID], this way they + can be retrieved easily + **kwargs: keywords arguments which can have the following keys, unset values are + equivalent to None: + jid_(jid.JID, None): jid to use for invitation, the jid will be created using + XEP-0077 + if the jid has no user part, an anonymous account will be used (no XMPP + account created in this case) + if None, automatically generate an account name (in the form + "invitation-[random UUID]@domain.tld") (note that this UUID is not the + same as the invitation one, as jid can be used publicly (leaking the + UUID), and invitation UUID give access to account. + in case of conflict, a suffix number is added to the account until a free + one if found (with a failure if SUFFIX_MAX is reached) + password(unicode, None): password to use (will be used for XMPP account and + profile) None to automatically generate one name(unicode, None): name of the invitee will be set as profile identity if present host_name(unicode, None): name of the host email(unicode, None): email to send the invitation to - if None, no invitation email is sent, you can still associate email using extra + if None, no invitation email is sent, you can still associate email using + extra if email is used, extra can't have "email" key - language(unicode): language of the invitee (used notabily to translate the invitation) + language(unicode): language of the invitee (used notabily to translate the + invitation) TODO: not used yet url_template(unicode, None): template to use to construct the invitation URL use {uuid} as a placeholder for identifier - use None if you don't want to include URL (or if it is already specified in custom message) + use None if you don't want to include URL (or if it is already specified + in custom message) /!\ you must put full URL, don't forget https:// - /!\ the URL will give access to the invitee account, you should warn in message to not publish it publicly - message_subject(unicode, None): customised message body for the invitation email + /!\ the URL will give access to the invitee account, you should warn in + message to not publish it publicly + message_subject(unicode, None): customised message body for the invitation + email None to use default subject uses the same substitution as for message_body message_body(unicode, None): customised message body for the invitation email @@ -169,7 +193,8 @@ ## initial checks extra = kwargs.pop('extra', {}) if set(kwargs).intersection(extra): - raise ValueError(_(u"You can't use following key(s) in both args and extra: {}").format( + raise ValueError( + _(u"You can't use following key(s) in both args and extra: {}").format( u', '.join(set(kwargs).intersection(extra)))) self.checkExtra(extra) @@ -177,10 +202,14 @@ email = kwargs.pop(u'email', None) emails_extra = kwargs.pop(u'emails_extra', []) if not email and emails_extra: - raise ValueError(_(u'You need to provide a main email address before using emails_extra')) + raise ValueError( + _(u'You need to provide a main email address before using emails_extra')) - if email is not None and not 'url_template' in kwargs and not 'message_body' in kwargs: - raise ValueError(_(u"You need to provide url_template if you use default message body")) + if (email is not None + and not 'url_template' in kwargs + and not 'message_body' in kwargs): + raise ValueError( + _(u"You need to provide url_template if you use default message body")) ## uuid log.info(_(u"creating an invitation")) @@ -195,8 +224,8 @@ # it is needed for invitation as the same password is used for profile # and SàT need to be able to automatically open the profile with the uuid # FIXME: we could add an extra encryption key which would be used with the uuid - # when the invitee is connecting (e.g. with URL). This key would not be saved - # and could be used to encrypt profile password. + # when the invitee is connecting (e.g. with URL). This key would not be + # saved and could be used to encrypt profile password. extra[KEY_PASSWORD] = password jid_ = kwargs.pop(u'jid_', None) @@ -205,10 +234,12 @@ if not domain: # TODO: fallback to profile's domain raise ValueError(_(u"You need to specify xmpp_domain in sat.conf")) - jid_ = u"invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), domain=domain) + jid_ = u"invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), + domain=domain) jid_ = jid.JID(jid_) if jid_.user: - # we don't register account if there is no user as anonymous login is then used + # we don't register account if there is no user as anonymous login is then + # used try: yield self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) except error.StanzaError as e: @@ -218,9 +249,11 @@ if idx >= SUFFIX_MAX: raise exceptions.ConflictError(_(u"Can't create XMPP account")) jid_.user = prefix + '_' + unicode(idx) - log.info(_(u"requested jid already exists, trying with {}".format(jid_.full()))) + log.info(_(u"requested jid already exists, trying with {}".format( + jid_.full()))) try: - yield self.host.plugins['XEP-0077'].registerNewAccount(jid_, password) + yield self.host.plugins['XEP-0077'].registerNewAccount(jid_, + password) except error.StanzaError as e: idx += 1 else: @@ -236,8 +269,10 @@ # profile creation should not fail as we generate unique name ourselves yield self.host.memory.createProfile(guest_profile, password) yield self.host.memory.startSession(password, guest_profile) - yield self.host.memory.setParam("JabberID", jid_.full(), "Connection", profile_key=guest_profile) - yield self.host.memory.setParam("Password", password, "Connection", profile_key=guest_profile) + yield self.host.memory.setParam("JabberID", jid_.full(), "Connection", + profile_key=guest_profile) + yield self.host.memory.setParam("Password", password, "Connection", + profile_key=guest_profile) name = kwargs.pop(u'name', None) if name is not None: extra[u'name'] = name @@ -288,7 +323,8 @@ yield sat_email.sendEmail( self.host, [email] + emails_extra, - (kwargs.pop(u'message_subject', None) or DEFAULT_SUBJECT).format(**format_args), + (kwargs.pop(u'message_subject', None) or DEFAULT_SUBJECT).format( + **format_args), (kwargs.pop(u'message_body', None) or DEFAULT_BODY).format(**format_args), ) @@ -312,7 +348,8 @@ return self.invitations[id_] def _modify(self, id_, new_extra, replace): - return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()}, replace) + return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()}, + replace) def modify(self, id_, new_extra, replace=False): """Modify invitation data @@ -366,6 +403,7 @@ """ invitations = yield self.invitations.items() if profile != C.PROF_KEY_NONE: - invitations = {id_:data for id_, data in invitations.iteritems() if data.get(u'profile') == profile} + invitations = {id_:data for id_, data in invitations.iteritems() + if data.get(u'profile') == profile} defer.returnValue(invitations)