From 754ac5092a3a37819a71f6565a1e54b3f2547940 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 30 Jun 2011 15:40:22 -0700 Subject: Reorganize features into plugins. --- sleekxmpp/features/__init__.py | 10 +++ sleekxmpp/features/feature_bind.py | 55 +++++++++++++++ sleekxmpp/features/feature_mechanisms.py | 116 +++++++++++++++++++++++++++++++ sleekxmpp/features/feature_session.py | 46 ++++++++++++ sleekxmpp/features/feature_starttls.py | 61 ++++++++++++++++ sleekxmpp/features/sasl_anonymous.py | 31 +++++++++ sleekxmpp/features/sasl_plain.py | 41 +++++++++++ 7 files changed, 360 insertions(+) create mode 100644 sleekxmpp/features/__init__.py create mode 100644 sleekxmpp/features/feature_bind.py create mode 100644 sleekxmpp/features/feature_mechanisms.py create mode 100644 sleekxmpp/features/feature_session.py create mode 100644 sleekxmpp/features/feature_starttls.py create mode 100644 sleekxmpp/features/sasl_anonymous.py create mode 100644 sleekxmpp/features/sasl_plain.py (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/__init__.py b/sleekxmpp/features/__init__.py new file mode 100644 index 00000000..940a37f1 --- /dev/null +++ b/sleekxmpp/features/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +__all__ = ['feature_starttls', 'feature_mechanisms', + 'sasl_plain', 'sasl_anonymous'] diff --git a/sleekxmpp/features/feature_bind.py b/sleekxmpp/features/feature_bind.py new file mode 100644 index 00000000..caa3844b --- /dev/null +++ b/sleekxmpp/features/feature_bind.py @@ -0,0 +1,55 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_bind(base_plugin): + + def plugin_init(self): + self.name = 'Bind Resource' + self.rfc = '6120' + self.description = 'Resource Binding Stream Feature' + + self.xmpp.register_feature('bind', + self._handle_bind_resource, + restart=False, + order=10000) + + def _handle_bind_resource(self, features): + """ + Handle requesting a specific resource. + + Arguments: + features -- The stream features stanza. + """ + log.debug("Requesting resource: %s" % self.xmpp.boundjid.resource) + iq = self.xmpp.Iq() + iq['type'] = 'set' + iq.enable('bind') + if self.xmpp.boundjid.resource: + iq['bind']['resource'] = self.xmpp.boundjid.resource + response = iq.send(now=True) + + self.xmpp.set_jid(response['bind']['jid']) + self.xmpp.bound = True + + log.info("Node set to: %s" % self.xmpp.boundjid.full) + + if 'session' not in features['features']: + log.debug("Established Session") + self.xmpp.sessionstarted = True + self.xmpp.session_started_event.set() + self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_mechanisms.py b/sleekxmpp/features/feature_mechanisms.py new file mode 100644 index 00000000..994c9bed --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms.py @@ -0,0 +1,116 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import stream +from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_mechanisms(base_plugin): + + def plugin_init(self): + self.name = 'SASL Mechanisms' + self.rfc = '6120' + self.description = "SASL Stream Feature" + + self.xmpp.register_stanza(stream.sasl.Success) + self.xmpp.register_stanza(stream.sasl.Failure) + self.xmpp.register_stanza(stream.sasl.Auth) + + self._mechanism_handlers = {} + self._mechanism_priorities = [] + + self.xmpp.register_handler( + Callback('SASL Success', + MatchXPath(stream.sasl.Success.tag_name()), + self._handle_success, + instream=True, + once=True)) + self.xmpp.register_handler( + Callback('SASL Failure', + MatchXPath(stream.sasl.Failure.tag_name()), + self._handle_fail, + instream=True, + once=True)) + + self.xmpp.register_feature('mechanisms', + self._handle_sasl_auth, + restart=True, + order=self.config.get('order', 100)) + + def register_mechanism(self, name, handler, priority=0): + """ + Register a handler for a SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism (all caps) + handler -- The function that will perform the + authentication. The function must + return True if it is able to carry + out the authentication, False if + a required condition is not met. + priority -- An integer value indicating the + preferred ordering for the mechanism. + High values will be attempted first. + """ + self._mechanism_handlers[name] = handler + self._mechanism_priorities.append((priority, name)) + self._mechanism_priorities.sort(reverse=True) + + def remove_mechanism(self, name): + """ + Remove support for a given SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism to remove (all caps) + """ + if name in self._mechanism_handlers: + del self._mechanism_handlers[name] + + p = self._mechanism_priorities + self._mechanism_priorities = [i for i in p if i[1] != name] + + def _handle_sasl_auth(self, features): + """ + Handle authenticating using SASL. + + Arguments: + features -- The stream features stanza. + """ + for priority, mech in self._mechanism_priorities: + if mech in features['mechanisms']: + log.debug('Attempt to use SASL %s' % mech) + if self._mechanism_handlers[mech](): + break + else: + log.error("No appropriate login method.") + self.xmpp.event("no_auth", direct=True) + self.xmpp.disconnect() + + return True + + def _handle_success(self, stanza): + """SASL authentication succeeded. Restart the stream.""" + self.xmpp.authenticated = True + self.xmpp.features.append('mechanisms') + raise RestartStream() + + def _handle_fail(self, stanza): + """SASL authentication failed. Disconnect and shutdown.""" + log.info("Authentication failed.") + self.xmpp.event("failed_auth", direct=True) + self.xmpp.disconnect() + log.debug("Starting SASL Auth") + return True diff --git a/sleekxmpp/features/feature_session.py b/sleekxmpp/features/feature_session.py new file mode 100644 index 00000000..5bae358c --- /dev/null +++ b/sleekxmpp/features/feature_session.py @@ -0,0 +1,46 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_session(base_plugin): + + def plugin_init(self): + self.name = 'Start Session' + self.rfc = '3920' + self.description = 'Start Session Stream Feature' + + self.xmpp.register_feature('session', + self._handle_start_session, + restart=False, + order=10001) + + def _handle_start_session(self, features): + """ + Handle the start of the session. + + Arguments: + feature -- The stream features element. + """ + iq = self.xmpp.Iq() + iq['type'] = 'set' + iq.enable('session') + response = iq.send(now=True) + + log.debug("Established Session") + self.xmpp.sessionstarted = True + self.xmpp.session_started_event.set() + self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_starttls.py b/sleekxmpp/features/feature_starttls.py new file mode 100644 index 00000000..5367fa49 --- /dev/null +++ b/sleekxmpp/features/feature_starttls.py @@ -0,0 +1,61 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza.stream import tls +from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_starttls(base_plugin): + + def plugin_init(self): + self.name = "STARTTLS" + self.rfc = '6120' + self.description = "STARTTLS Stream Feature" + + self.xmpp.register_stanza(tls.Proceed) + self.xmpp.register_handler( + Callback('STARTTLS Proceed', + MatchXPath(tls.Proceed.tag_name()), + self._handle_starttls_proceed, + instream=True)) + self.xmpp.register_feature('starttls', + self._handle_starttls, + restart=True, + order=self.config.get('order', 0)) + + def _handle_starttls(self, features): + """ + Handle notification that the server supports TLS. + + Arguments: + features -- The stream:features element. + """ + if not self.xmpp.use_tls: + return False + elif self.xmpp.ssl_support: + self.xmpp.send(features['starttls'], now=True) + return True + else: + log.warning("The module tlslite is required to log in" +\ + " to some servers, and has not been found.") + return False + + def _handle_starttls_proceed(self, proceed): + """Restart the XML stream when TLS is accepted.""" + log.debug("Starting TLS") + if self.xmpp.start_tls(): + self.xmpp.features.append('starttls') + raise RestartStream() diff --git a/sleekxmpp/features/sasl_anonymous.py b/sleekxmpp/features/sasl_anonymous.py new file mode 100644 index 00000000..469d9d19 --- /dev/null +++ b/sleekxmpp/features/sasl_anonymous.py @@ -0,0 +1,31 @@ +import base64 +import sys +import logging + +from sleekxmpp.stanza.stream import sasl +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class sasl_anonymous(base_plugin): + + def plugin_init(self): + self.name = 'SASL ANONYMOUS' + self.rfc = '6120' + self.description = 'SASL ANONYMOUS Mechanism' + + self.xmpp.register_sasl_mechanism('ANONYMOUS', + self._handle_anonymous, + priority=self.config.get('priority', 0)) + + def _handle_anonymous(self): + if self.xmpp.boundjid.user: + return False + + resp = sasl.Auth(self.xmpp) + resp['mechanism'] = 'ANONYMOUS' + resp.send(now=True) + + return True diff --git a/sleekxmpp/features/sasl_plain.py b/sleekxmpp/features/sasl_plain.py new file mode 100644 index 00000000..36c7d9df --- /dev/null +++ b/sleekxmpp/features/sasl_plain.py @@ -0,0 +1,41 @@ +import base64 +import sys +import logging + +from sleekxmpp.stanza.stream import sasl +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class sasl_plain(base_plugin): + + def plugin_init(self): + self.name = 'SASL PLAIN' + self.rfc = '6120' + self.description = 'SASL PLAIN Mechanism' + + self.xmpp.register_sasl_mechanism('PLAIN', + self._handle_plain, + priority=self.config.get('priority', 1)) + + def _handle_plain(self): + if not self.xmpp.boundjid.user: + return False + + if sys.version_info < (3, 0): + user = bytes(self.xmpp.boundjid.user) + password = bytes(self.xmpp.password) + else: + user = bytes(self.xmpp.boundjid.user, 'utf-8') + password = bytes(self.xmpp.password, 'utf-8') + + auth = base64.b64encode(b'\x00' + user + \ + b'\x00' + password).decode('utf-8') + + resp = sasl.Auth(self.xmpp) + resp['mechanism'] = 'PLAIN' + resp['value'] = auth + resp.send(now=True) + return True -- cgit v1.2.3 From 634f5d691bab9855deddc4c201389bb60470d76e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 1 Jul 2011 14:45:55 -0700 Subject: Continued reorganization and streamlining. --- sleekxmpp/features/__init__.py | 1 + sleekxmpp/features/feature_bind.py | 55 ---------- sleekxmpp/features/feature_bind/__init__.py | 10 ++ sleekxmpp/features/feature_bind/bind.py | 62 +++++++++++ sleekxmpp/features/feature_bind/stanza.py | 22 ++++ sleekxmpp/features/feature_mechanisms.py | 116 --------------------- sleekxmpp/features/feature_mechanisms/__init__.py | 10 ++ .../features/feature_mechanisms/mechanisms.py | 116 +++++++++++++++++++++ sleekxmpp/features/feature_mechanisms/stanza.py | 104 ++++++++++++++++++ sleekxmpp/features/feature_session.py | 46 -------- sleekxmpp/features/feature_session/__init__.py | 10 ++ sleekxmpp/features/feature_session/session.py | 54 ++++++++++ sleekxmpp/features/feature_session/stanza.py | 21 ++++ sleekxmpp/features/feature_starttls.py | 61 ----------- sleekxmpp/features/feature_starttls/__init__.py | 10 ++ sleekxmpp/features/feature_starttls/stanza.py | 47 +++++++++ sleekxmpp/features/feature_starttls/starttls.py | 66 ++++++++++++ 17 files changed, 533 insertions(+), 278 deletions(-) delete mode 100644 sleekxmpp/features/feature_bind.py create mode 100644 sleekxmpp/features/feature_bind/__init__.py create mode 100644 sleekxmpp/features/feature_bind/bind.py create mode 100644 sleekxmpp/features/feature_bind/stanza.py delete mode 100644 sleekxmpp/features/feature_mechanisms.py create mode 100644 sleekxmpp/features/feature_mechanisms/__init__.py create mode 100644 sleekxmpp/features/feature_mechanisms/mechanisms.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza.py delete mode 100644 sleekxmpp/features/feature_session.py create mode 100644 sleekxmpp/features/feature_session/__init__.py create mode 100644 sleekxmpp/features/feature_session/session.py create mode 100644 sleekxmpp/features/feature_session/stanza.py delete mode 100644 sleekxmpp/features/feature_starttls.py create mode 100644 sleekxmpp/features/feature_starttls/__init__.py create mode 100644 sleekxmpp/features/feature_starttls/stanza.py create mode 100644 sleekxmpp/features/feature_starttls/starttls.py (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/__init__.py b/sleekxmpp/features/__init__.py index 940a37f1..65d2bdbf 100644 --- a/sleekxmpp/features/__init__.py +++ b/sleekxmpp/features/__init__.py @@ -7,4 +7,5 @@ """ __all__ = ['feature_starttls', 'feature_mechanisms', + 'feature_bind', 'feature_session', 'sasl_plain', 'sasl_anonymous'] diff --git a/sleekxmpp/features/feature_bind.py b/sleekxmpp/features/feature_bind.py deleted file mode 100644 index caa3844b..00000000 --- a/sleekxmpp/features/feature_bind.py +++ /dev/null @@ -1,55 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -import logging - -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class feature_bind(base_plugin): - - def plugin_init(self): - self.name = 'Bind Resource' - self.rfc = '6120' - self.description = 'Resource Binding Stream Feature' - - self.xmpp.register_feature('bind', - self._handle_bind_resource, - restart=False, - order=10000) - - def _handle_bind_resource(self, features): - """ - Handle requesting a specific resource. - - Arguments: - features -- The stream features stanza. - """ - log.debug("Requesting resource: %s" % self.xmpp.boundjid.resource) - iq = self.xmpp.Iq() - iq['type'] = 'set' - iq.enable('bind') - if self.xmpp.boundjid.resource: - iq['bind']['resource'] = self.xmpp.boundjid.resource - response = iq.send(now=True) - - self.xmpp.set_jid(response['bind']['jid']) - self.xmpp.bound = True - - log.info("Node set to: %s" % self.xmpp.boundjid.full) - - if 'session' not in features['features']: - log.debug("Established Session") - self.xmpp.sessionstarted = True - self.xmpp.session_started_event.set() - self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_bind/__init__.py b/sleekxmpp/features/feature_bind/__init__.py new file mode 100644 index 00000000..fce94dd6 --- /dev/null +++ b/sleekxmpp/features/feature_bind/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.features.feature_bind.bind import feature_bind +from sleekxmpp.features.feature_bind.stanza import Bind diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py new file mode 100644 index 00000000..e177d7b2 --- /dev/null +++ b/sleekxmpp/features/feature_bind/bind.py @@ -0,0 +1,62 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import Iq, StreamFeatures +from sleekxmpp.features.feature_bind import stanza +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_bind(base_plugin): + + def plugin_init(self): + self.name = 'Bind Resource' + self.rfc = '6120' + self.description = 'Resource Binding Stream Feature' + self.stanza = stanza + + self.xmpp.register_feature('bind', + self._handle_bind_resource, + restart=False, + order=10000) + + register_stanza_plugin(Iq, stanza.Bind) + register_stanza_plugin(StreamFeatures, stanza.Bind) + + def _handle_bind_resource(self, features): + """ + Handle requesting a specific resource. + + Arguments: + features -- The stream features stanza. + """ + log.debug("Requesting resource: %s" % self.xmpp.boundjid.resource) + iq = self.xmpp.Iq() + iq['type'] = 'set' + iq.enable('bind') + if self.xmpp.boundjid.resource: + iq['bind']['resource'] = self.xmpp.boundjid.resource + response = iq.send(now=True) + + self.xmpp.set_jid(response['bind']['jid']) + self.xmpp.bound = True + + log.info("Node set to: %s" % self.xmpp.boundjid.full) + + if 'session' not in features['features']: + log.debug("Established Session") + self.xmpp.sessionstarted = True + self.xmpp.session_started_event.set() + self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_bind/stanza.py b/sleekxmpp/features/feature_bind/stanza.py new file mode 100644 index 00000000..f3e025fa --- /dev/null +++ b/sleekxmpp/features/feature_bind/stanza.py @@ -0,0 +1,22 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import Iq, StreamFeatures +from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin + + +class Bind(ElementBase): + + """ + """ + + name = 'bind' + namespace = 'urn:ietf:params:xml:ns:xmpp-bind' + interfaces = set(('resource', 'jid')) + sub_interfaces = interfaces + plugin_attrib = 'bind' diff --git a/sleekxmpp/features/feature_mechanisms.py b/sleekxmpp/features/feature_mechanisms.py deleted file mode 100644 index 994c9bed..00000000 --- a/sleekxmpp/features/feature_mechanisms.py +++ /dev/null @@ -1,116 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -import logging - -from sleekxmpp.stanza import stream -from sleekxmpp.xmlstream import RestartStream -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class feature_mechanisms(base_plugin): - - def plugin_init(self): - self.name = 'SASL Mechanisms' - self.rfc = '6120' - self.description = "SASL Stream Feature" - - self.xmpp.register_stanza(stream.sasl.Success) - self.xmpp.register_stanza(stream.sasl.Failure) - self.xmpp.register_stanza(stream.sasl.Auth) - - self._mechanism_handlers = {} - self._mechanism_priorities = [] - - self.xmpp.register_handler( - Callback('SASL Success', - MatchXPath(stream.sasl.Success.tag_name()), - self._handle_success, - instream=True, - once=True)) - self.xmpp.register_handler( - Callback('SASL Failure', - MatchXPath(stream.sasl.Failure.tag_name()), - self._handle_fail, - instream=True, - once=True)) - - self.xmpp.register_feature('mechanisms', - self._handle_sasl_auth, - restart=True, - order=self.config.get('order', 100)) - - def register_mechanism(self, name, handler, priority=0): - """ - Register a handler for a SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism (all caps) - handler -- The function that will perform the - authentication. The function must - return True if it is able to carry - out the authentication, False if - a required condition is not met. - priority -- An integer value indicating the - preferred ordering for the mechanism. - High values will be attempted first. - """ - self._mechanism_handlers[name] = handler - self._mechanism_priorities.append((priority, name)) - self._mechanism_priorities.sort(reverse=True) - - def remove_mechanism(self, name): - """ - Remove support for a given SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism to remove (all caps) - """ - if name in self._mechanism_handlers: - del self._mechanism_handlers[name] - - p = self._mechanism_priorities - self._mechanism_priorities = [i for i in p if i[1] != name] - - def _handle_sasl_auth(self, features): - """ - Handle authenticating using SASL. - - Arguments: - features -- The stream features stanza. - """ - for priority, mech in self._mechanism_priorities: - if mech in features['mechanisms']: - log.debug('Attempt to use SASL %s' % mech) - if self._mechanism_handlers[mech](): - break - else: - log.error("No appropriate login method.") - self.xmpp.event("no_auth", direct=True) - self.xmpp.disconnect() - - return True - - def _handle_success(self, stanza): - """SASL authentication succeeded. Restart the stream.""" - self.xmpp.authenticated = True - self.xmpp.features.append('mechanisms') - raise RestartStream() - - def _handle_fail(self, stanza): - """SASL authentication failed. Disconnect and shutdown.""" - log.info("Authentication failed.") - self.xmpp.event("failed_auth", direct=True) - self.xmpp.disconnect() - log.debug("Starting SASL Auth") - return True diff --git a/sleekxmpp/features/feature_mechanisms/__init__.py b/sleekxmpp/features/feature_mechanisms/__init__.py new file mode 100644 index 00000000..a93b2b6f --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.features.feature_mechanisms.mechanisms import feature_mechanisms +from sleekxmpp.features.feature_mechanisms.stanza import * diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py new file mode 100644 index 00000000..994c9bed --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -0,0 +1,116 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import stream +from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_mechanisms(base_plugin): + + def plugin_init(self): + self.name = 'SASL Mechanisms' + self.rfc = '6120' + self.description = "SASL Stream Feature" + + self.xmpp.register_stanza(stream.sasl.Success) + self.xmpp.register_stanza(stream.sasl.Failure) + self.xmpp.register_stanza(stream.sasl.Auth) + + self._mechanism_handlers = {} + self._mechanism_priorities = [] + + self.xmpp.register_handler( + Callback('SASL Success', + MatchXPath(stream.sasl.Success.tag_name()), + self._handle_success, + instream=True, + once=True)) + self.xmpp.register_handler( + Callback('SASL Failure', + MatchXPath(stream.sasl.Failure.tag_name()), + self._handle_fail, + instream=True, + once=True)) + + self.xmpp.register_feature('mechanisms', + self._handle_sasl_auth, + restart=True, + order=self.config.get('order', 100)) + + def register_mechanism(self, name, handler, priority=0): + """ + Register a handler for a SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism (all caps) + handler -- The function that will perform the + authentication. The function must + return True if it is able to carry + out the authentication, False if + a required condition is not met. + priority -- An integer value indicating the + preferred ordering for the mechanism. + High values will be attempted first. + """ + self._mechanism_handlers[name] = handler + self._mechanism_priorities.append((priority, name)) + self._mechanism_priorities.sort(reverse=True) + + def remove_mechanism(self, name): + """ + Remove support for a given SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism to remove (all caps) + """ + if name in self._mechanism_handlers: + del self._mechanism_handlers[name] + + p = self._mechanism_priorities + self._mechanism_priorities = [i for i in p if i[1] != name] + + def _handle_sasl_auth(self, features): + """ + Handle authenticating using SASL. + + Arguments: + features -- The stream features stanza. + """ + for priority, mech in self._mechanism_priorities: + if mech in features['mechanisms']: + log.debug('Attempt to use SASL %s' % mech) + if self._mechanism_handlers[mech](): + break + else: + log.error("No appropriate login method.") + self.xmpp.event("no_auth", direct=True) + self.xmpp.disconnect() + + return True + + def _handle_success(self, stanza): + """SASL authentication succeeded. Restart the stream.""" + self.xmpp.authenticated = True + self.xmpp.features.append('mechanisms') + raise RestartStream() + + def _handle_fail(self, stanza): + """SASL authentication failed. Disconnect and shutdown.""" + log.info("Authentication failed.") + self.xmpp.event("failed_auth", direct=True) + self.xmpp.disconnect() + log.debug("Starting SASL Auth") + return True diff --git a/sleekxmpp/features/feature_mechanisms/stanza.py b/sleekxmpp/features/feature_mechanisms/stanza.py new file mode 100644 index 00000000..e55a72ad --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza.py @@ -0,0 +1,104 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Mechanisms(ElementBase): + + """ + """ + + name = 'mechanisms' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('mechanisms', 'required')) + plugin_attrib = name + is_extension = True + + def get_required(self): + """ + """ + return True + + def get_mechanisms(self): + """ + """ + results = [] + mechs = self.findall('{%s}mechanism' % self.namespace) + if mechs: + for mech in mechs: + results.append(mech.text) + return results + + def set_mechanisms(self, values): + """ + """ + self.del_mechanisms() + for val in values: + mech = ET.Element('{%s}mechanism' % self.namespace) + mech.text = val + self.append(mech) + + def del_mechanisms(self): + """ + """ + mechs = self.findall('{%s}mechanism' % self.namespace) + if mechs: + for mech in mechs: + self.xml.remove(mech) + + +class Success(StanzaBase): + + """ + """ + + name = 'success' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set() + plugin_attrib = name + + +class Failure(StanzaBase): + + """ + """ + + name = 'failure' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set() + plugin_attrib = name + + +class Auth(StanzaBase): + + """ + """ + + name = 'auth' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('mechanism', 'value')) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def set_value(self, value): + self.xml.text = value + + def get_value(self): + return self.xml.text + + def del_value(self): + self.xml.text = '' + + +register_stanza_plugin(StreamFeatures, Mechanisms) diff --git a/sleekxmpp/features/feature_session.py b/sleekxmpp/features/feature_session.py deleted file mode 100644 index 5bae358c..00000000 --- a/sleekxmpp/features/feature_session.py +++ /dev/null @@ -1,46 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -import logging - -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class feature_session(base_plugin): - - def plugin_init(self): - self.name = 'Start Session' - self.rfc = '3920' - self.description = 'Start Session Stream Feature' - - self.xmpp.register_feature('session', - self._handle_start_session, - restart=False, - order=10001) - - def _handle_start_session(self, features): - """ - Handle the start of the session. - - Arguments: - feature -- The stream features element. - """ - iq = self.xmpp.Iq() - iq['type'] = 'set' - iq.enable('session') - response = iq.send(now=True) - - log.debug("Established Session") - self.xmpp.sessionstarted = True - self.xmpp.session_started_event.set() - self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_session/__init__.py b/sleekxmpp/features/feature_session/__init__.py new file mode 100644 index 00000000..1399f73b --- /dev/null +++ b/sleekxmpp/features/feature_session/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.features.feature_session.session import feature_session +from sleekxmpp.features.feature_session.stanza import Session diff --git a/sleekxmpp/features/feature_session/session.py b/sleekxmpp/features/feature_session/session.py new file mode 100644 index 00000000..4d17b2d2 --- /dev/null +++ b/sleekxmpp/features/feature_session/session.py @@ -0,0 +1,54 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import Iq, StreamFeatures +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + +from sleekxmpp.features.feature_session import stanza + + +log = logging.getLogger(__name__) + + +class feature_session(base_plugin): + + def plugin_init(self): + self.name = 'Start Session' + self.rfc = '3920' + self.description = 'Start Session Stream Feature' + self.stanza = stanza + + self.xmpp.register_feature('session', + self._handle_start_session, + restart=False, + order=10001) + + register_stanza_plugin(Iq, stanza.Session) + register_stanza_plugin(StreamFeatures, stanza.Session) + + def _handle_start_session(self, features): + """ + Handle the start of the session. + + Arguments: + feature -- The stream features element. + """ + iq = self.xmpp.Iq() + iq['type'] = 'set' + iq.enable('session') + response = iq.send(now=True) + + log.debug("Established Session") + self.xmpp.sessionstarted = True + self.xmpp.session_started_event.set() + self.xmpp.event("session_start") diff --git a/sleekxmpp/features/feature_session/stanza.py b/sleekxmpp/features/feature_session/stanza.py new file mode 100644 index 00000000..2047a4f0 --- /dev/null +++ b/sleekxmpp/features/feature_session/stanza.py @@ -0,0 +1,21 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import Iq, StreamFeatures +from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin + + +class Session(ElementBase): + + """ + """ + + name = 'session' + namespace = 'urn:ietf:params:xml:ns:xmpp-session' + interfaces = set() + plugin_attrib = 'session' diff --git a/sleekxmpp/features/feature_starttls.py b/sleekxmpp/features/feature_starttls.py deleted file mode 100644 index 5367fa49..00000000 --- a/sleekxmpp/features/feature_starttls.py +++ /dev/null @@ -1,61 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -import logging - -from sleekxmpp.stanza.stream import tls -from sleekxmpp.xmlstream import RestartStream -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class feature_starttls(base_plugin): - - def plugin_init(self): - self.name = "STARTTLS" - self.rfc = '6120' - self.description = "STARTTLS Stream Feature" - - self.xmpp.register_stanza(tls.Proceed) - self.xmpp.register_handler( - Callback('STARTTLS Proceed', - MatchXPath(tls.Proceed.tag_name()), - self._handle_starttls_proceed, - instream=True)) - self.xmpp.register_feature('starttls', - self._handle_starttls, - restart=True, - order=self.config.get('order', 0)) - - def _handle_starttls(self, features): - """ - Handle notification that the server supports TLS. - - Arguments: - features -- The stream:features element. - """ - if not self.xmpp.use_tls: - return False - elif self.xmpp.ssl_support: - self.xmpp.send(features['starttls'], now=True) - return True - else: - log.warning("The module tlslite is required to log in" +\ - " to some servers, and has not been found.") - return False - - def _handle_starttls_proceed(self, proceed): - """Restart the XML stream when TLS is accepted.""" - log.debug("Starting TLS") - if self.xmpp.start_tls(): - self.xmpp.features.append('starttls') - raise RestartStream() diff --git a/sleekxmpp/features/feature_starttls/__init__.py b/sleekxmpp/features/feature_starttls/__init__.py new file mode 100644 index 00000000..042e37fa --- /dev/null +++ b/sleekxmpp/features/feature_starttls/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.features.feature_starttls.starttls import feature_starttls +from sleekxmpp.features.feature_starttls.stanza import * diff --git a/sleekxmpp/features/feature_starttls/stanza.py b/sleekxmpp/features/feature_starttls/stanza.py new file mode 100644 index 00000000..5fdafabd --- /dev/null +++ b/sleekxmpp/features/feature_starttls/stanza.py @@ -0,0 +1,47 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import StanzaBase, ElementBase +from sleekxmpp.xmlstream import register_stanza_plugin + + +class STARTTLS(ElementBase): + + """ + """ + + name = 'starttls' + namespace = 'urn:ietf:params:xml:ns:xmpp-tls' + interfaces = set(('required',)) + plugin_attrib = name + + def get_required(self): + """ + """ + return True + + +class Proceed(StanzaBase): + + """ + """ + + name = 'proceed' + namespace = 'urn:ietf:params:xml:ns:xmpp-tls' + interfaces = set() + + +class Failure(StanzaBase): + + """ + """ + + name = 'failure' + namespace = 'urn:ietf:params:xml:ns:xmpp-tls' + interfaces = set() diff --git a/sleekxmpp/features/feature_starttls/starttls.py b/sleekxmpp/features/feature_starttls/starttls.py new file mode 100644 index 00000000..cbb94be0 --- /dev/null +++ b/sleekxmpp/features/feature_starttls/starttls.py @@ -0,0 +1,66 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.features.feature_starttls import stanza + + +log = logging.getLogger(__name__) + + +class feature_starttls(base_plugin): + + def plugin_init(self): + self.name = "STARTTLS" + self.rfc = '6120' + self.description = "STARTTLS Stream Feature" + self.stanza = stanza + + self.xmpp.register_handler( + Callback('STARTTLS Proceed', + MatchXPath(stanza.Proceed.tag_name()), + self._handle_starttls_proceed, + instream=True)) + self.xmpp.register_feature('starttls', + self._handle_starttls, + restart=True, + order=self.config.get('order', 0)) + + self.xmpp.register_stanza(stanza.Proceed) + self.xmpp.register_stanza(stanza.Failure) + register_stanza_plugin(StreamFeatures, stanza.STARTTLS) + + def _handle_starttls(self, features): + """ + Handle notification that the server supports TLS. + + Arguments: + features -- The stream:features element. + """ + if not self.xmpp.use_tls: + return False + elif self.xmpp.ssl_support: + self.xmpp.send(features['starttls'], now=True) + return True + else: + log.warning("The module tlslite is required to log in" +\ + " to some servers, and has not been found.") + return False + + def _handle_starttls_proceed(self, proceed): + """Restart the XML stream when TLS is accepted.""" + log.debug("Starting TLS") + if self.xmpp.start_tls(): + self.xmpp.features.append('starttls') + raise RestartStream() -- cgit v1.2.3 From b0297af38d6dcd9ebfdaa0131ea798c9fe2b8c63 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 21:43:02 -0700 Subject: Finish cleaning up stream feature organization. Fixed missing references that weren't caught due to leftover pyc file allowing tests to keep working when they shouldn't have. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 13 +++++++------ sleekxmpp/features/sasl_anonymous.py | 3 +-- sleekxmpp/features/sasl_plain.py | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 994c9bed..3cdb1b0a 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,11 +8,11 @@ import logging -from sleekxmpp.stanza import stream from sleekxmpp.xmlstream import RestartStream from sleekxmpp.xmlstream.matcher import * from sleekxmpp.xmlstream.handler import * from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.features.feature_mechanisms import stanza log = logging.getLogger(__name__) @@ -24,23 +24,24 @@ class feature_mechanisms(base_plugin): self.name = 'SASL Mechanisms' self.rfc = '6120' self.description = "SASL Stream Feature" + self.stanza = stanza - self.xmpp.register_stanza(stream.sasl.Success) - self.xmpp.register_stanza(stream.sasl.Failure) - self.xmpp.register_stanza(stream.sasl.Auth) + self.xmpp.register_stanza(stanza.Success) + self.xmpp.register_stanza(stanza.Failure) + self.xmpp.register_stanza(stanza.Auth) self._mechanism_handlers = {} self._mechanism_priorities = [] self.xmpp.register_handler( Callback('SASL Success', - MatchXPath(stream.sasl.Success.tag_name()), + MatchXPath(stanza.Success.tag_name()), self._handle_success, instream=True, once=True)) self.xmpp.register_handler( Callback('SASL Failure', - MatchXPath(stream.sasl.Failure.tag_name()), + MatchXPath(stanza.Failure.tag_name()), self._handle_fail, instream=True, once=True)) diff --git a/sleekxmpp/features/sasl_anonymous.py b/sleekxmpp/features/sasl_anonymous.py index 469d9d19..71a4b2e5 100644 --- a/sleekxmpp/features/sasl_anonymous.py +++ b/sleekxmpp/features/sasl_anonymous.py @@ -2,7 +2,6 @@ import base64 import sys import logging -from sleekxmpp.stanza.stream import sasl from sleekxmpp.plugins.base import base_plugin @@ -24,7 +23,7 @@ class sasl_anonymous(base_plugin): if self.xmpp.boundjid.user: return False - resp = sasl.Auth(self.xmpp) + resp = self.xmpp['feature_sasl'].stanza.Auth(self.xmpp) resp['mechanism'] = 'ANONYMOUS' resp.send(now=True) diff --git a/sleekxmpp/features/sasl_plain.py b/sleekxmpp/features/sasl_plain.py index 36c7d9df..270d28fe 100644 --- a/sleekxmpp/features/sasl_plain.py +++ b/sleekxmpp/features/sasl_plain.py @@ -2,7 +2,6 @@ import base64 import sys import logging -from sleekxmpp.stanza.stream import sasl from sleekxmpp.plugins.base import base_plugin @@ -34,7 +33,7 @@ class sasl_plain(base_plugin): auth = base64.b64encode(b'\x00' + user + \ b'\x00' + password).decode('utf-8') - resp = sasl.Auth(self.xmpp) + resp = self.xmpp['feature_mechanisms'].stanza.Auth(self.xmpp) resp['mechanism'] = 'PLAIN' resp['value'] = auth resp.send(now=True) -- cgit v1.2.3 From fba235a801a3a1c06d1769cdc944b72dce33f88a Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 21:57:50 -0700 Subject: Simplify SASL mech registration. Moved SASL registration completely to the feature plugin, instead of keeping a portion of it in ClientXMPP. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 4 ++-- sleekxmpp/features/sasl_anonymous.py | 5 +++-- sleekxmpp/features/sasl_plain.py | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 3cdb1b0a..a8a046e4 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -51,7 +51,7 @@ class feature_mechanisms(base_plugin): restart=True, order=self.config.get('order', 100)) - def register_mechanism(self, name, handler, priority=0): + def register(self, name, handler, priority=0): """ Register a handler for a SASL authentication mechanism. @@ -70,7 +70,7 @@ class feature_mechanisms(base_plugin): self._mechanism_priorities.append((priority, name)) self._mechanism_priorities.sort(reverse=True) - def remove_mechanism(self, name): + def remove(self, name): """ Remove support for a given SASL authentication mechanism. diff --git a/sleekxmpp/features/sasl_anonymous.py b/sleekxmpp/features/sasl_anonymous.py index 71a4b2e5..98a0d36e 100644 --- a/sleekxmpp/features/sasl_anonymous.py +++ b/sleekxmpp/features/sasl_anonymous.py @@ -14,8 +14,9 @@ class sasl_anonymous(base_plugin): self.name = 'SASL ANONYMOUS' self.rfc = '6120' self.description = 'SASL ANONYMOUS Mechanism' + self.stanza = self.xmpp['feature_mechanisms'].stanza - self.xmpp.register_sasl_mechanism('ANONYMOUS', + self.xmpp['feature_mechanisms'].register('ANONYMOUS', self._handle_anonymous, priority=self.config.get('priority', 0)) @@ -23,7 +24,7 @@ class sasl_anonymous(base_plugin): if self.xmpp.boundjid.user: return False - resp = self.xmpp['feature_sasl'].stanza.Auth(self.xmpp) + resp = self.stanza.Auth(self.xmpp) resp['mechanism'] = 'ANONYMOUS' resp.send(now=True) diff --git a/sleekxmpp/features/sasl_plain.py b/sleekxmpp/features/sasl_plain.py index 270d28fe..427660ab 100644 --- a/sleekxmpp/features/sasl_plain.py +++ b/sleekxmpp/features/sasl_plain.py @@ -14,8 +14,9 @@ class sasl_plain(base_plugin): self.name = 'SASL PLAIN' self.rfc = '6120' self.description = 'SASL PLAIN Mechanism' + self.stanza = self.xmpp['feature_mechanisms'].stanza - self.xmpp.register_sasl_mechanism('PLAIN', + self.xmpp['feature_mechanisms'].register('PLAIN', self._handle_plain, priority=self.config.get('priority', 1)) @@ -33,7 +34,7 @@ class sasl_plain(base_plugin): auth = base64.b64encode(b'\x00' + user + \ b'\x00' + password).decode('utf-8') - resp = self.xmpp['feature_mechanisms'].stanza.Auth(self.xmpp) + resp = self.stanza.Auth(self.xmpp) resp['mechanism'] = 'PLAIN' resp['value'] = auth resp.send(now=True) -- cgit v1.2.3 From b898b14b77d739cb1c118c9e3648aa268348d293 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 22:30:34 -0700 Subject: Use a set to track negotiated features. Added guards to prevent renegotiating STARTTLS or SASL in cases where servers don't behave properly. --- sleekxmpp/features/feature_bind/bind.py | 2 ++ sleekxmpp/features/feature_mechanisms/mechanisms.py | 7 ++++++- sleekxmpp/features/feature_session/session.py | 2 ++ sleekxmpp/features/feature_starttls/starttls.py | 8 ++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py index e177d7b2..c5d9395f 100644 --- a/sleekxmpp/features/feature_bind/bind.py +++ b/sleekxmpp/features/feature_bind/bind.py @@ -53,6 +53,8 @@ class feature_bind(base_plugin): self.xmpp.set_jid(response['bind']['jid']) self.xmpp.bound = True + self.features.add('bind') + log.info("Node set to: %s" % self.xmpp.boundjid.full) if 'session' not in features['features']: diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index a8a046e4..011010fb 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -90,6 +90,11 @@ class feature_mechanisms(base_plugin): Arguments: features -- The stream features stanza. """ + if 'mechanisms' in self.xmpp.features: + # SASL authentication has already succeeded, but the + # server has incorrectly offered it again. + return False + for priority, mech in self._mechanism_priorities: if mech in features['mechanisms']: log.debug('Attempt to use SASL %s' % mech) @@ -105,7 +110,7 @@ class feature_mechanisms(base_plugin): def _handle_success(self, stanza): """SASL authentication succeeded. Restart the stream.""" self.xmpp.authenticated = True - self.xmpp.features.append('mechanisms') + self.xmpp.features.add('mechanisms') raise RestartStream() def _handle_fail(self, stanza): diff --git a/sleekxmpp/features/feature_session/session.py b/sleekxmpp/features/feature_session/session.py index 4d17b2d2..9c5e0448 100644 --- a/sleekxmpp/features/feature_session/session.py +++ b/sleekxmpp/features/feature_session/session.py @@ -48,6 +48,8 @@ class feature_session(base_plugin): iq.enable('session') response = iq.send(now=True) + self.xmpp.features.add('session') + log.debug("Established Session") self.xmpp.sessionstarted = True self.xmpp.session_started_event.set() diff --git a/sleekxmpp/features/feature_starttls/starttls.py b/sleekxmpp/features/feature_starttls/starttls.py index cbb94be0..841e7a8d 100644 --- a/sleekxmpp/features/feature_starttls/starttls.py +++ b/sleekxmpp/features/feature_starttls/starttls.py @@ -48,7 +48,11 @@ class feature_starttls(base_plugin): Arguments: features -- The stream:features element. """ - if not self.xmpp.use_tls: + if 'starttls' in self.xmpp.features: + # We have already negotiated TLS, but the server is + # offering it again, against spec. + return False + elif not self.xmpp.use_tls: return False elif self.xmpp.ssl_support: self.xmpp.send(features['starttls'], now=True) @@ -62,5 +66,5 @@ class feature_starttls(base_plugin): """Restart the XML stream when TLS is accepted.""" log.debug("Starting TLS") if self.xmpp.start_tls(): - self.xmpp.features.append('starttls') + self.xmpp.features.add('starttls') raise RestartStream() -- cgit v1.2.3 From 219df582dab2a5dd3c9e2bbfef27d3cfa814841d Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 22:49:34 -0700 Subject: It isn't 2010 anymore. I keep forgetting to update the copyright on new code. --- sleekxmpp/features/__init__.py | 2 +- sleekxmpp/features/feature_bind/__init__.py | 2 +- sleekxmpp/features/feature_bind/bind.py | 2 +- sleekxmpp/features/feature_bind/stanza.py | 2 +- sleekxmpp/features/feature_mechanisms/__init__.py | 2 +- sleekxmpp/features/feature_mechanisms/mechanisms.py | 2 +- sleekxmpp/features/feature_session/__init__.py | 2 +- sleekxmpp/features/feature_session/session.py | 2 +- sleekxmpp/features/feature_session/stanza.py | 2 +- sleekxmpp/features/feature_starttls/__init__.py | 2 +- sleekxmpp/features/feature_starttls/stanza.py | 2 +- sleekxmpp/features/feature_starttls/starttls.py | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/__init__.py b/sleekxmpp/features/__init__.py index 65d2bdbf..5c86cfea 100644 --- a/sleekxmpp/features/__init__.py +++ b/sleekxmpp/features/__init__.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_bind/__init__.py b/sleekxmpp/features/feature_bind/__init__.py index fce94dd6..aa854f87 100644 --- a/sleekxmpp/features/feature_bind/__init__.py +++ b/sleekxmpp/features/feature_bind/__init__.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py index c5d9395f..0b0f2033 100644 --- a/sleekxmpp/features/feature_bind/bind.py +++ b/sleekxmpp/features/feature_bind/bind.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_bind/stanza.py b/sleekxmpp/features/feature_bind/stanza.py index f3e025fa..2c1484e0 100644 --- a/sleekxmpp/features/feature_bind/stanza.py +++ b/sleekxmpp/features/feature_bind/stanza.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_mechanisms/__init__.py b/sleekxmpp/features/feature_mechanisms/__init__.py index a93b2b6f..b0b9dcc1 100644 --- a/sleekxmpp/features/feature_mechanisms/__init__.py +++ b/sleekxmpp/features/feature_mechanisms/__init__.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 011010fb..210267fa 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_session/__init__.py b/sleekxmpp/features/feature_session/__init__.py index 1399f73b..3c84baed 100644 --- a/sleekxmpp/features/feature_session/__init__.py +++ b/sleekxmpp/features/feature_session/__init__.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_session/session.py b/sleekxmpp/features/feature_session/session.py index 9c5e0448..0daec5da 100644 --- a/sleekxmpp/features/feature_session/session.py +++ b/sleekxmpp/features/feature_session/session.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_session/stanza.py b/sleekxmpp/features/feature_session/stanza.py index 2047a4f0..40ea583d 100644 --- a/sleekxmpp/features/feature_session/stanza.py +++ b/sleekxmpp/features/feature_session/stanza.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_starttls/__init__.py b/sleekxmpp/features/feature_starttls/__init__.py index 042e37fa..4ae89433 100644 --- a/sleekxmpp/features/feature_starttls/__init__.py +++ b/sleekxmpp/features/feature_starttls/__init__.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_starttls/stanza.py b/sleekxmpp/features/feature_starttls/stanza.py index 5fdafabd..8b09ad94 100644 --- a/sleekxmpp/features/feature_starttls/stanza.py +++ b/sleekxmpp/features/feature_starttls/stanza.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. diff --git a/sleekxmpp/features/feature_starttls/starttls.py b/sleekxmpp/features/feature_starttls/starttls.py index 841e7a8d..639788a0 100644 --- a/sleekxmpp/features/feature_starttls/starttls.py +++ b/sleekxmpp/features/feature_starttls/starttls.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. -- cgit v1.2.3 From 0224d028e76ba608400fe55602fdb84f8e70f13b Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 23:09:29 -0700 Subject: SASL failure event now includes the failure stanza. Broke SASL stanzas into separate files. Fixed typo in feature_bind. --- sleekxmpp/features/feature_bind/bind.py | 2 +- sleekxmpp/features/feature_mechanisms/__init__.py | 5 +- .../features/feature_mechanisms/mechanisms.py | 9 +- sleekxmpp/features/feature_mechanisms/stanza.py | 104 --------------------- .../features/feature_mechanisms/stanza/__init__.py | 14 +++ .../features/feature_mechanisms/stanza/auth.py | 35 +++++++ .../features/feature_mechanisms/stanza/failure.py | 76 +++++++++++++++ .../feature_mechanisms/stanza/mechanisms.py | 55 +++++++++++ .../features/feature_mechanisms/stanza/success.py | 22 +++++ 9 files changed, 212 insertions(+), 110 deletions(-) delete mode 100644 sleekxmpp/features/feature_mechanisms/stanza.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/__init__.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/auth.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/failure.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/mechanisms.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/success.py (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py index 0b0f2033..de03192c 100644 --- a/sleekxmpp/features/feature_bind/bind.py +++ b/sleekxmpp/features/feature_bind/bind.py @@ -53,7 +53,7 @@ class feature_bind(base_plugin): self.xmpp.set_jid(response['bind']['jid']) self.xmpp.bound = True - self.features.add('bind') + self.xmpp.features.add('bind') log.info("Node set to: %s" % self.xmpp.boundjid.full) diff --git a/sleekxmpp/features/feature_mechanisms/__init__.py b/sleekxmpp/features/feature_mechanisms/__init__.py index b0b9dcc1..5379ef4e 100644 --- a/sleekxmpp/features/feature_mechanisms/__init__.py +++ b/sleekxmpp/features/feature_mechanisms/__init__.py @@ -7,4 +7,7 @@ """ from sleekxmpp.features.feature_mechanisms.mechanisms import feature_mechanisms -from sleekxmpp.features.feature_mechanisms.stanza import * +from sleekxmpp.features.feature_mechanisms.stanza import Mechanisms +from sleekxmpp.features.feature_mechanisms.stanza import Auth +from sleekxmpp.features.feature_mechanisms.stanza import Success +from sleekxmpp.features.feature_mechanisms.stanza import Failure diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 210267fa..7a877793 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,7 +8,8 @@ import logging -from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin from sleekxmpp.xmlstream.matcher import * from sleekxmpp.xmlstream.handler import * from sleekxmpp.plugins.base import base_plugin @@ -26,6 +27,7 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza + register_stanza_plugin(StreamFeatures, stanza.Mechanisms) self.xmpp.register_stanza(stanza.Success) self.xmpp.register_stanza(stanza.Failure) self.xmpp.register_stanza(stanza.Auth) @@ -115,8 +117,7 @@ class feature_mechanisms(base_plugin): def _handle_fail(self, stanza): """SASL authentication failed. Disconnect and shutdown.""" - log.info("Authentication failed.") - self.xmpp.event("failed_auth", direct=True) + log.info("Authentication failed: %s" % stanza['condition']) + self.xmpp.event("failed_auth", stanza, direct=True) self.xmpp.disconnect() - log.debug("Starting SASL Auth") return True diff --git a/sleekxmpp/features/feature_mechanisms/stanza.py b/sleekxmpp/features/feature_mechanisms/stanza.py deleted file mode 100644 index e55a72ad..00000000 --- a/sleekxmpp/features/feature_mechanisms/stanza.py +++ /dev/null @@ -1,104 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -from sleekxmpp.stanza import StreamFeatures -from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET -from sleekxmpp.xmlstream import register_stanza_plugin - - -class Mechanisms(ElementBase): - - """ - """ - - name = 'mechanisms' - namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' - interfaces = set(('mechanisms', 'required')) - plugin_attrib = name - is_extension = True - - def get_required(self): - """ - """ - return True - - def get_mechanisms(self): - """ - """ - results = [] - mechs = self.findall('{%s}mechanism' % self.namespace) - if mechs: - for mech in mechs: - results.append(mech.text) - return results - - def set_mechanisms(self, values): - """ - """ - self.del_mechanisms() - for val in values: - mech = ET.Element('{%s}mechanism' % self.namespace) - mech.text = val - self.append(mech) - - def del_mechanisms(self): - """ - """ - mechs = self.findall('{%s}mechanism' % self.namespace) - if mechs: - for mech in mechs: - self.xml.remove(mech) - - -class Success(StanzaBase): - - """ - """ - - name = 'success' - namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' - interfaces = set() - plugin_attrib = name - - -class Failure(StanzaBase): - - """ - """ - - name = 'failure' - namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' - interfaces = set() - plugin_attrib = name - - -class Auth(StanzaBase): - - """ - """ - - name = 'auth' - namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' - interfaces = set(('mechanism', 'value')) - plugin_attrib = name - - def setup(self, xml): - StanzaBase.setup(self, xml) - self.xml.tag = self.tag_name() - - def set_value(self, value): - self.xml.text = value - - def get_value(self): - return self.xml.text - - def del_value(self): - self.xml.text = '' - - -register_stanza_plugin(StreamFeatures, Mechanisms) diff --git a/sleekxmpp/features/feature_mechanisms/stanza/__init__.py b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py new file mode 100644 index 00000000..0d9135d3 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py @@ -0,0 +1,14 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + + +from sleekxmpp.features.feature_mechanisms.stanza.mechanisms import Mechanisms +from sleekxmpp.features.feature_mechanisms.stanza.auth import Auth +from sleekxmpp.features.feature_mechanisms.stanza.success import Success +from sleekxmpp.features.feature_mechanisms.stanza.failure import Failure + diff --git a/sleekxmpp/features/feature_mechanisms/stanza/auth.py b/sleekxmpp/features/feature_mechanisms/stanza/auth.py new file mode 100644 index 00000000..12208841 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/auth.py @@ -0,0 +1,35 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Auth(StanzaBase): + + """ + """ + + name = 'auth' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('mechanism', 'value')) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def set_value(self, value): + self.xml.text = value + + def get_value(self): + return self.xml.text + + def del_value(self): + self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/failure.py b/sleekxmpp/features/feature_mechanisms/stanza/failure.py new file mode 100644 index 00000000..98a1ab80 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/failure.py @@ -0,0 +1,76 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Failure(StanzaBase): + + """ + """ + + name = 'failure' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('condition', 'text')) + plugin_attrib = name + sub_interfaces = set(('text',)) + conditions = set(('aborted', 'account-disabled', 'credentials-expired', + 'encryption-required', 'incorrect-encoding', 'invalid-authzid', + 'invalid-mechanism', 'malformed-request', 'mechansism-too-weak', + 'not-authorized', 'temporary-auth-failure')) + + def setup(self, xml=None): + """ + Populate the stanza object using an optional XML object. + + Overrides ElementBase.setup. + + Sets a default error type and condition, and changes the + parent stanza's type to 'error'. + + Arguments: + xml -- Use an existing XML object for the stanza's values. + """ + # StanzaBase overrides self.namespace + self.namespace = Failure.namespace + + if StanzaBase.setup(self, xml): + #If we had to generate XML then set default values. + self['condition'] = 'not-authorized' + + def get_condition(self): + """Return the condition element's name.""" + for child in self.xml.getchildren(): + if "{%s}" % self.namespace in child.tag: + cond = child.tag.split('}', 1)[-1] + if cond in self.conditions: + return cond + return 'not-authorized' + + def set_condition(self, value): + """ + Set the tag name of the condition element. + + Arguments: + value -- The tag name of the condition element. + """ + if value in self.conditions: + del self['condition'] + self.xml.append(ET.Element("{%s}%s" % (self.namespace, value))) + return self + + def del_condition(self): + """Remove the condition element.""" + for child in self.xml.getchildren(): + if "{%s}" % self.condition_ns in child.tag: + tag = child.tag.split('}', 1)[-1] + if tag in self.conditions: + self.xml.remove(child) + return self diff --git a/sleekxmpp/features/feature_mechanisms/stanza/mechanisms.py b/sleekxmpp/features/feature_mechanisms/stanza/mechanisms.py new file mode 100644 index 00000000..1189cd80 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/mechanisms.py @@ -0,0 +1,55 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Mechanisms(ElementBase): + + """ + """ + + name = 'mechanisms' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('mechanisms', 'required')) + plugin_attrib = name + is_extension = True + + def get_required(self): + """ + """ + return True + + def get_mechanisms(self): + """ + """ + results = [] + mechs = self.findall('{%s}mechanism' % self.namespace) + if mechs: + for mech in mechs: + results.append(mech.text) + return results + + def set_mechanisms(self, values): + """ + """ + self.del_mechanisms() + for val in values: + mech = ET.Element('{%s}mechanism' % self.namespace) + mech.text = val + self.append(mech) + + def del_mechanisms(self): + """ + """ + mechs = self.findall('{%s}mechanism' % self.namespace) + if mechs: + for mech in mechs: + self.xml.remove(mech) diff --git a/sleekxmpp/features/feature_mechanisms/stanza/success.py b/sleekxmpp/features/feature_mechanisms/stanza/success.py new file mode 100644 index 00000000..2c40f56c --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/success.py @@ -0,0 +1,22 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Success(StanzaBase): + + """ + """ + + name = 'success' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set() + plugin_attrib = name -- cgit v1.2.3 From d4091dbde641dc9796b51e032ea23a0ba5c1fcbb Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 3 Aug 2011 17:00:51 -0700 Subject: Integrate a modified version of Dave Cridland's Suelta SASL library. --- .../features/feature_mechanisms/mechanisms.py | 86 +++++++++++----------- .../features/feature_mechanisms/stanza/__init__.py | 3 +- .../features/feature_mechanisms/stanza/auth.py | 12 ++- .../feature_mechanisms/stanza/challenge.py | 39 ++++++++++ .../features/feature_mechanisms/stanza/failure.py | 2 + .../features/feature_mechanisms/stanza/response.py | 39 ++++++++++ .../features/feature_mechanisms/stanza/success.py | 4 + sleekxmpp/features/sasl_anonymous.py | 31 -------- sleekxmpp/features/sasl_plain.py | 41 ----------- 9 files changed, 139 insertions(+), 118 deletions(-) create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/challenge.py create mode 100644 sleekxmpp/features/feature_mechanisms/stanza/response.py delete mode 100644 sleekxmpp/features/sasl_anonymous.py delete mode 100644 sleekxmpp/features/sasl_plain.py (limited to 'sleekxmpp/features') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 7a877793..d60818bb 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,6 +8,8 @@ import logging +from sleekxmpp.thirdparty import suelta + from sleekxmpp.stanza import StreamFeatures from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin from sleekxmpp.xmlstream.matcher import * @@ -27,13 +29,35 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza + + def tls_active(): + return 'starttls' in self.xmpp.features + + def basic_callback(mech, values): + if 'username' in values: + values['username'] = self.xmpp.boundjid.user + if 'password' in values: + values['password'] = self.xmpp.password + mech.fulfill(values) + + sasl_callback = self.config.get('sasl_callback', None) + if sasl_callback is None: + sasl_callback = basic_callback + + self.mech = None + self.sasl = suelta.SASL(self.xmpp.boundjid.domain, 'xmpp', + username=self.xmpp.boundjid.user, + sec_query=suelta.sec_query_allow, + request_values=sasl_callback, + tls_active=tls_active) + register_stanza_plugin(StreamFeatures, stanza.Mechanisms) + self.xmpp.register_stanza(stanza.Success) self.xmpp.register_stanza(stanza.Failure) self.xmpp.register_stanza(stanza.Auth) - - self._mechanism_handlers = {} - self._mechanism_priorities = [] + self.xmpp.register_stanza(stanza.Challenge) + self.xmpp.register_stanza(stanza.Response) self.xmpp.register_handler( Callback('SASL Success', @@ -47,44 +71,16 @@ class feature_mechanisms(base_plugin): self._handle_fail, instream=True, once=True)) + self.xmpp.register_handler( + Callback('SASL Challenge', + MatchXPath(stanza.Challenge.tag_name()), + self._handle_challenge)) self.xmpp.register_feature('mechanisms', self._handle_sasl_auth, restart=True, order=self.config.get('order', 100)) - def register(self, name, handler, priority=0): - """ - Register a handler for a SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism (all caps) - handler -- The function that will perform the - authentication. The function must - return True if it is able to carry - out the authentication, False if - a required condition is not met. - priority -- An integer value indicating the - preferred ordering for the mechanism. - High values will be attempted first. - """ - self._mechanism_handlers[name] = handler - self._mechanism_priorities.append((priority, name)) - self._mechanism_priorities.sort(reverse=True) - - def remove(self, name): - """ - Remove support for a given SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism to remove (all caps) - """ - if name in self._mechanism_handlers: - del self._mechanism_handlers[name] - - p = self._mechanism_priorities - self._mechanism_priorities = [i for i in p if i[1] != name] - def _handle_sasl_auth(self, features): """ Handle authenticating using SASL. @@ -97,18 +93,26 @@ class feature_mechanisms(base_plugin): # server has incorrectly offered it again. return False - for priority, mech in self._mechanism_priorities: - if mech in features['mechanisms']: - log.debug('Attempt to use SASL %s' % mech) - if self._mechanism_handlers[mech](): - break + mech_list = features['mechanisms'] + self.mech = self.sasl.choose_mechanism(mech_list) + + if self.mech is not None: + resp = stanza.Auth(self.xmpp) + resp['mechanism'] = self.mech.name + resp['value'] = self.mech.process() + resp.send(now=True) else: log.error("No appropriate login method.") self.xmpp.event("no_auth", direct=True) self.xmpp.disconnect() - return True + def _handle_challenge(self, stanza): + """SASL challenge received. Process and send response.""" + resp = self.stanza.Response(self.xmpp) + resp['value'] = self.mech.process(stanza['value']) + resp.send(now=True) + def _handle_success(self, stanza): """SASL authentication succeeded. Restart the stream.""" self.xmpp.authenticated = True diff --git a/sleekxmpp/features/feature_mechanisms/stanza/__init__.py b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py index 0d9135d3..8b80f358 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/__init__.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py @@ -11,4 +11,5 @@ from sleekxmpp.features.feature_mechanisms.stanza.mechanisms import Mechanisms from sleekxmpp.features.feature_mechanisms.stanza.auth import Auth from sleekxmpp.features.feature_mechanisms.stanza.success import Success from sleekxmpp.features.feature_mechanisms.stanza.failure import Failure - +from sleekxmpp.features.feature_mechanisms.stanza.challenge import Challenge +from sleekxmpp.features.feature_mechanisms.stanza.response import Response diff --git a/sleekxmpp/features/feature_mechanisms/stanza/auth.py b/sleekxmpp/features/feature_mechanisms/stanza/auth.py index 12208841..e069b57f 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/auth.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/auth.py @@ -6,6 +6,10 @@ See the file LICENSE for copying permission. """ +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + from sleekxmpp.stanza import StreamFeatures from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET from sleekxmpp.xmlstream import register_stanza_plugin @@ -25,11 +29,11 @@ class Auth(StanzaBase): StanzaBase.setup(self, xml) self.xml.tag = self.tag_name() - def set_value(self, value): - self.xml.text = value - def get_value(self): - return self.xml.text + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') def del_value(self): self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/challenge.py b/sleekxmpp/features/feature_mechanisms/stanza/challenge.py new file mode 100644 index 00000000..82af869f --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/challenge.py @@ -0,0 +1,39 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Challenge(StanzaBase): + + """ + """ + + name = 'challenge' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('value',)) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def get_value(self): + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') + + def del_value(self): + self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/failure.py b/sleekxmpp/features/feature_mechanisms/stanza/failure.py index 98a1ab80..027cc5af 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/failure.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/failure.py @@ -45,6 +45,8 @@ class Failure(StanzaBase): #If we had to generate XML then set default values. self['condition'] = 'not-authorized' + self.xml.tag = self.tag_name() + def get_condition(self): """Return the condition element's name.""" for child in self.xml.getchildren(): diff --git a/sleekxmpp/features/feature_mechanisms/stanza/response.py b/sleekxmpp/features/feature_mechanisms/stanza/response.py new file mode 100644 index 00000000..45bb8207 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/response.py @@ -0,0 +1,39 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Response(StanzaBase): + + """ + """ + + name = 'response' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('value',)) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def get_value(self): + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') + + def del_value(self): + self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/success.py b/sleekxmpp/features/feature_mechanisms/stanza/success.py index 2c40f56c..028e28a3 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/success.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/success.py @@ -20,3 +20,7 @@ class Success(StanzaBase): namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' interfaces = set() plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() diff --git a/sleekxmpp/features/sasl_anonymous.py b/sleekxmpp/features/sasl_anonymous.py deleted file mode 100644 index 98a0d36e..00000000 --- a/sleekxmpp/features/sasl_anonymous.py +++ /dev/null @@ -1,31 +0,0 @@ -import base64 -import sys -import logging - -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class sasl_anonymous(base_plugin): - - def plugin_init(self): - self.name = 'SASL ANONYMOUS' - self.rfc = '6120' - self.description = 'SASL ANONYMOUS Mechanism' - self.stanza = self.xmpp['feature_mechanisms'].stanza - - self.xmpp['feature_mechanisms'].register('ANONYMOUS', - self._handle_anonymous, - priority=self.config.get('priority', 0)) - - def _handle_anonymous(self): - if self.xmpp.boundjid.user: - return False - - resp = self.stanza.Auth(self.xmpp) - resp['mechanism'] = 'ANONYMOUS' - resp.send(now=True) - - return True diff --git a/sleekxmpp/features/sasl_plain.py b/sleekxmpp/features/sasl_plain.py deleted file mode 100644 index 427660ab..00000000 --- a/sleekxmpp/features/sasl_plain.py +++ /dev/null @@ -1,41 +0,0 @@ -import base64 -import sys -import logging - -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class sasl_plain(base_plugin): - - def plugin_init(self): - self.name = 'SASL PLAIN' - self.rfc = '6120' - self.description = 'SASL PLAIN Mechanism' - self.stanza = self.xmpp['feature_mechanisms'].stanza - - self.xmpp['feature_mechanisms'].register('PLAIN', - self._handle_plain, - priority=self.config.get('priority', 1)) - - def _handle_plain(self): - if not self.xmpp.boundjid.user: - return False - - if sys.version_info < (3, 0): - user = bytes(self.xmpp.boundjid.user) - password = bytes(self.xmpp.password) - else: - user = bytes(self.xmpp.boundjid.user, 'utf-8') - password = bytes(self.xmpp.password, 'utf-8') - - auth = base64.b64encode(b'\x00' + user + \ - b'\x00' + password).decode('utf-8') - - resp = self.stanza.Auth(self.xmpp) - resp['mechanism'] = 'PLAIN' - resp['value'] = auth - resp.send(now=True) - return True -- cgit v1.2.3