comparison src/plugins/plugin_xep_0070.py @ 2005:2afd5bd781ef

plugin XEP-0070: implementation of XEP-0070 (verifying HTTP request via XMPP)
author Geoffrey POUZET <chteufleur@kingpenguin.tk>
date Mon, 11 Jul 2016 20:01:05 +0200
parents
children 0694a2611bad
comparison
equal deleted inserted replaced
2004:99ec7b66daa9 2005:2afd5bd781ef
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # SAT plugin for managing xep-0070
5 # Copyright (C) 2009-2016 Geoffrey POUZET (chteufleur@kingpenguin.tk)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 from sat.core.i18n import _, D_
20 from sat.core.constants import Const as C
21 from sat.core.i18n import _
22 from sat.core.log import getLogger
23 from twisted.words.protocols.jabber import xmlstream
24 from twisted.words.protocols import jabber
25 log = getLogger(__name__)
26 from sat.tools import xml_tools
27
28 from wokkel import disco, iwokkel
29 from zope.interface import implements
30 try:
31 from twisted.words.protocols.xmlstream import XMPPHandler
32 except ImportError:
33 from wokkel.subprotocols import XMPPHandler
34
35
36 NS_HTTP_AUTH = 'http://jabber.org/protocol/http-auth'
37
38 IQ = 'iq'
39 IQ_GET = '/'+IQ+'[@type="get"]'
40 IQ_HTTP_AUTH_REQUEST = IQ_GET + '/confirm[@xmlns="' + NS_HTTP_AUTH + '"]'
41
42 MSG = 'message'
43 MSG_GET = '/'+MSG+'[@type="normal"]'
44 MSG_HTTP_AUTH_REQUEST = MSG_GET + '/confirm[@xmlns="' + NS_HTTP_AUTH + '"]'
45
46
47 PLUGIN_INFO = {
48 "name": "XEP-0070 Plugin",
49 "import_name": "XEP-0070",
50 "type": "XEP",
51 "protocols": ["XEP-0070"],
52 "dependencies": [],
53 "main": "XEP_0070",
54 "handler": "yes",
55 "description": _("""Implementation of HTTP Requests via XMPP""")
56 }
57
58
59 class XEP_0070(object):
60 """
61 Implementation for XEP 0070.
62 """
63
64 def __init__(self, host):
65 log.info(_("Plugin XEP_0070 initialization"))
66 self.host = host
67 self._dictRequest = dict()
68
69 def getHandler(self, profile):
70 return XEP_0070_handler(self, profile)
71
72 def onHttpAuthRequestIQ(self, iq_elt, client):
73 """This method is called on confirmation request received (XEP-0070 #4.5)
74 @param iq_elt: IQ element
75 @param client: %(doc_client)s"""
76 log.info(_("XEP-0070 Verifying HTTP Requests via XMPP (iq)"))
77 self._treatHttpAuthRequest(iq_elt, IQ, client)
78
79 def onHttpAuthRequestMsg(self, msg_elt, client):
80 """This method is called on confirmation request received (XEP-0070 #4.5)
81 @param msg_elt: message element
82 @param client: %(doc_client)s"""
83 log.info(_("XEP-0070 Verifying HTTP Requests via XMPP (message)"))
84 self._treatHttpAuthRequest(msg_elt, MSG, client)
85
86 def _treatHttpAuthRequest(self, elt, stanzaType, client):
87 elt.handled = True
88 auth_elt = elt.elements(NS_HTTP_AUTH, 'confirm').next()
89 auth_id = auth_elt['id']
90 auth_method = auth_elt['method']
91 auth_url = auth_elt['url']
92 self._dictRequest[client] = (auth_id, auth_method, auth_url, stanzaType, elt)
93
94 confirm_ui = xml_tools.XMLUI("form", title=D_("Auth confirmation"), submit_id='')
95 confirm_ui.addText(D_("HTTP ({}) Authorization for {} (id: {}).".format(auth_method, auth_url, auth_id)))
96 confirm_ui.addText(D_("Submit to authorize, cancel otherwise."))
97 d = xml_tools.deferredUI(self.host, confirm_ui, chained=False)
98 d.addCallback(self._authRequestCallback, client.profile)
99 self.host.actionNew({"xmlui": confirm_ui.toXml()}, profile=client.profile)
100
101 def _authRequestCallback(self, result, profile):
102 client = self.host.getClient(profile)
103 try:
104 cancelled = result['cancelled']
105 except KeyError:
106 cancelled = False
107
108 authorized = False
109
110 if cancelled:
111 auth_id, auth_method, auth_url, stanzaType, elt = self._dictRequest[client]
112 del self._dictRequest[client]
113 authorized = False
114 else:
115 try:
116 auth_id, auth_method, auth_url, stanzaType, elt = self._dictRequest[client]
117 del self._dictRequest[client]
118 authorized = True
119 except KeyError:
120 authorized = False
121
122 if authorized:
123 if (stanzaType == IQ):
124 # iq
125 log.debug(_("XEP-0070 reply iq"))
126 iq_result_elt = xmlstream.toResponse(elt, 'result')
127 client.xmlstream.send(iq_result_elt)
128 elif (stanzaType == MSG):
129 # message
130 log.debug(_("XEP-0070 reply message"))
131 msg_result_elt = xmlstream.toResponse(elt, 'result')
132 msg_result_elt.addChild(elt.elements(NS_HTTP_AUTH, 'confirm').next())
133 client.xmlstream.send(msg_result_elt)
134 else:
135 log.debug(_("XEP-0070 reply error"))
136 result_elt = jabber.error.StanzaError("not-authorized").toResponse(elt)
137 client.xmlstream.send(result_elt)
138
139
140 class XEP_0070_handler(XMPPHandler):
141 implements(iwokkel.IDisco)
142
143 def __init__(self, plugin_parent, profile):
144 self.plugin_parent = plugin_parent
145 self.host = plugin_parent.host
146 self.profile = profile
147
148 def connectionInitialized(self):
149 self.xmlstream.addObserver(IQ_HTTP_AUTH_REQUEST, self.plugin_parent.onHttpAuthRequestIQ, client=self.parent)
150 self.xmlstream.addObserver(MSG_HTTP_AUTH_REQUEST, self.plugin_parent.onHttpAuthRequestMsg, client=self.parent)
151
152 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
153 return [disco.DiscoFeature(NS_HTTP_AUTH)]
154
155 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
156 return []