source:
ralphm-patches/client_listen_authenticator.patch
@
54:03ec57713c90
Last change on this file since 54:03ec57713c90 was 54:03ec57713c90, checked in by Ralph Meijer <ralphm@…>, 9 years ago | |
---|---|
File size: 6.6 KB |
-
wokkel/client.py
# HG changeset patch # Parent 661689a96e34ac375d95c612d36d959eee3927a9 Add authenticator for accepting XMPP client connections. The new authenticator XMPPClientListenAuthenticator is to be used together with an `XmlStream` created for an incoming XMPP stream. It handles the SASL PLAIN mechanism only. This authenticator needs a backend service to hold the domain served and relay established connections. Upon binding a resource, its `bindResource` method is called with the local-part, domain and resource bound to the new stream. TODO: * Add tests. * Add docstrings. * Readd stream namespace check? * Host checks. * Password checks. * Support for multiple domains? diff -r 661689a96e34 wokkel/client.py
a b 10 10 that should probably eventually move there. 11 11 """ 12 12 13 import base64 14 13 15 from twisted.application import service 14 16 from twisted.internet import reactor 15 17 from twisted.names.srvconnect import SRVConnector 16 from twisted.words.protocols.jabber import client, sasl, xmlstream18 from twisted.words.protocols.jabber import client, error, sasl, xmlstream 17 19 18 20 from wokkel import generic 19 21 from wokkel.subprotocols import StreamManager 20 22 23 NS_CLIENT = 'jabber:client' 24 25 XPATH_ALL = "/*" 26 XPATH_AUTH = "/auth[@xmlns='%s']" % sasl.NS_XMPP_SASL 27 XPATH_BIND = "/iq[@type='set']/bind[@xmlns='%s']" % client.NS_XMPP_BIND 28 XPATH_SESSION = "/iq[@type='set']/session[@xmlns='%s']" % \ 29 client.NS_XMPP_SESSION 30 21 31 class CheckAuthInitializer(object): 22 32 """ 23 33 Check what authentication methods are available. … … 51 61 autentication. 52 62 """ 53 63 54 namespace = 'jabber:client'64 namespace = NS_CLIENT 55 65 56 66 def __init__(self, jid, password): 57 67 xmlstream.ConnectAuthenticator.__init__(self, jid.host) … … 186 196 c = XMPPClientConnector(reactor, domain, factory) 187 197 c.connect() 188 198 return factory.deferred 199 200 201 202 class XMPPClientListenAuthenticator(xmlstream.ListenAuthenticator): 203 namespace = NS_CLIENT 204 205 def __init__(self, service): 206 self.service = service 207 self.failureGrace = 3 208 self.state = 'auth' 209 210 211 def associateWithStream(self, xs): 212 xmlstream.ListenAuthenticator.associateWithStream(self, xs) 213 self.xmlstream.addObserver(XPATH_ALL, self.onElementFallback, -1) 214 215 216 def onElementFallback(self, element): 217 if element.handled: 218 return 219 220 exc = error.StreamError('not-authorized') 221 self.xmlstream.sendStreamError(exc) 222 223 224 def streamStarted(self, rootElement): 225 xmlstream.ListenAuthenticator.streamStarted(self, rootElement) 226 227 # check namespace 228 #if self.xmlstream.namespace != self.namespace: 229 # self.xmlstream.namespace = self.namespace 230 # exc = error.StreamError('invalid-namespace') 231 # self.xmlstream.sendStreamError(exc) 232 # return 233 234 # TODO: check domain (self.service.domain) 235 236 self.xmlstream.sendHeader() 237 238 try: 239 stateHandlerName = 'streamStarted_' + self.state 240 stateHandler = getattr(self, stateHandlerName) 241 except AttributeError: 242 log.msg('streamStarted handler for', self.state, 'not found') 243 else: 244 stateHandler() 245 246 247 def toState(self, state): 248 self.state = state 249 if state == 'initialized': 250 self.xmlstream.removeObserver(XPATH_ALL, self.onElementFallback) 251 self.xmlstream.addOnetimeObserver(XPATH_SESSION, self.onSession, 1) 252 self.xmlstream.dispatch(self.xmlstream, 253 xmlstream.STREAM_AUTHD_EVENT) 254 255 256 def streamStarted_auth(self): 257 features = domish.Element((xmlstream.NS_STREAMS, 'features')) 258 features.addElement((sasl.NS_XMPP_SASL, 'mechanisms')) 259 features.mechanisms.addElement('mechanism', content='PLAIN') 260 self.xmlstream.send(features) 261 self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth) 262 263 264 def onAuth(self, auth): 265 auth.handled = True 266 267 if auth.getAttribute('mechanism') != 'PLAIN': 268 failure = domish.Element((sasl.NS_XMPP_SASL, 'failure')) 269 failure.addElement('invalid-mechanism') 270 self.xmlstream.send(failure) 271 272 # Close stream on too many failing authentication attempts 273 self.failureGrace -= 1 274 if self.failureGrace == 0: 275 self.xmlstream.sendFooter() 276 else: 277 self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth) 278 279 return 280 281 initialResponse = base64.b64decode(unicode(auth)) 282 authzid, authcid, passwd = initialResponse.split('\x00') 283 284 # TODO: check passwd 285 286 # authenticated 287 288 self.username = authcid 289 290 success = domish.Element((sasl.NS_XMPP_SASL, 'success')) 291 self.xmlstream.send(success) 292 self.xmlstream.reset() 293 294 self.toState('bind') 295 296 297 def streamStarted_bind(self): 298 features = domish.Element((xmlstream.NS_STREAMS, 'features')) 299 features.addElement((client.NS_XMPP_BIND, 'bind')) 300 features.addElement((client.NS_XMPP_SESSION, 'session')) 301 self.xmlstream.send(features) 302 self.xmlstream.addOnetimeObserver(XPATH_BIND, self.onBind) 303 304 305 def onBind(self, iq): 306 def cb(boundJID): 307 self.xmlstream.otherEntity = boundJID 308 self.toState('initialized') 309 310 response = xmlstream.toResponse(iq, 'result') 311 response.addElement((client.NS_XMPP_BIND, 'bind')) 312 response.bind.addElement((client.NS_XMPP_BIND, 'jid'), 313 content=boundJID.full()) 314 315 return response 316 317 def eb(failure): 318 if not isinstance(failure, error.StanzaError): 319 log.msg(failure) 320 exc = error.StanzaError('internal-server-error') 321 else: 322 exc = failure.value 323 324 return exc.toResponse(iq) 325 326 iq.handled = True 327 resource = unicode(iq.bind) or None 328 d = self.service.bindResource(self.username, 329 self.service.domain, 330 resource) 331 d.addCallback(cb) 332 d.addErrback(eb) 333 d.addCallback(self.xmlstream.send) 334 335 336 def onSession(self, iq): 337 iq.handled = True 338 339 reply = domish.Element((None, 'iq')) 340 reply['type'] = 'result' 341 if iq.getAttribute('id'): 342 reply['id'] = iq['id'] 343 reply.addElement((client.NS_XMPP_SESSION, 'session')) 344 self.xmlstream.send(reply) 345 346 347
Note: See TracBrowser
for help on using the repository browser.