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