You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2014/09/25 16:40:32 UTC

svn commit: r1627554 - in /qpid/trunk/qpid: python/qpid/ python/qpid/saslmech/ python/qpid/tests/ python/qpid/tests/saslmech/ tests/src/py/qpid_tests/broker_0_9/

Author: kwall
Date: Thu Sep 25 14:40:31 2014
New Revision: 1627554

URL: http://svn.apache.org/r1627554
Log:
QPID-6116: [Python Client 08..091] Add ability to negotiate SASL mechanism and add pure python implementations for SCRAM/CRAM/PLAIN mechanisms

Added:
    qpid/trunk/qpid/python/qpid/saslmech/
    qpid/trunk/qpid/python/qpid/saslmech/__init__.py
    qpid/trunk/qpid/python/qpid/saslmech/amqplain.py
    qpid/trunk/qpid/python/qpid/saslmech/anonymous.py
    qpid/trunk/qpid/python/qpid/saslmech/cram_md5.py
    qpid/trunk/qpid/python/qpid/saslmech/cram_md5_hex.py
    qpid/trunk/qpid/python/qpid/saslmech/external.py
    qpid/trunk/qpid/python/qpid/saslmech/finder.py
    qpid/trunk/qpid/python/qpid/saslmech/plain.py
    qpid/trunk/qpid/python/qpid/saslmech/sasl.py
      - copied, changed from r1627553, qpid/trunk/qpid/python/qpid/tests/__init__.py
    qpid/trunk/qpid/python/qpid/saslmech/scram.py
    qpid/trunk/qpid/python/qpid/saslmech/scram_sha_1.py
    qpid/trunk/qpid/python/qpid/saslmech/scram_sha_256.py
    qpid/trunk/qpid/python/qpid/tests/saslmech/
    qpid/trunk/qpid/python/qpid/tests/saslmech/__init__.py
    qpid/trunk/qpid/python/qpid/tests/saslmech/finder.py
    qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl.py
    qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl2.py
Modified:
    qpid/trunk/qpid/python/qpid/client.py
    qpid/trunk/qpid/python/qpid/testlib.py
    qpid/trunk/qpid/python/qpid/tests/__init__.py
    qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/echo.py

Modified: qpid/trunk/qpid/python/qpid/client.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/client.py?rev=1627554&r1=1627553&r2=1627554&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/client.py (original)
+++ qpid/trunk/qpid/python/qpid/client.py Thu Sep 25 14:40:31 2014
@@ -30,6 +30,8 @@ from connection08 import Connection, Fra
 from spec08 import load
 from queue import Queue
 from reference import ReferenceId, References
+from saslmech.finder import get_sasl_mechanism
+from saslmech.sasl import SaslException
 
 
 class Client:
@@ -48,6 +50,7 @@ class Client:
     self.mechanism = None
     self.response = None
     self.locale = None
+    self.sasl = None
 
     self.vhost = vhost
     if self.vhost == None:
@@ -77,12 +80,17 @@ class Client:
       self.lock.release()
     return q
 
-  def start(self, response, mechanism="AMQPLAIN", locale="en_US", tune_params=None, client_properties=None, connection_options=None):
+  def start(self, response=None, mechanism=None, locale="en_US", tune_params=None,
+            username=None, password=None,
+            client_properties=None, connection_options=None, sasl_options = None):
     self.mechanism = mechanism
     self.response = response
+    self.username = username
+    self.password = password
     self.locale = locale
     self.tune_params = tune_params
     self.client_properties=get_client_properties_with_defaults(provided_client_properties=client_properties)
+    self.sasl_options = sasl_options
     self.socket = connect(self.host, self.port, connection_options)
     self.conn = Connection(self.socket, self.spec)
     self.peer = Peer(self.conn, ClientDelegate(self), Session)
@@ -127,11 +135,32 @@ class ClientDelegate(Delegate):
     self.client = client
 
   def connection_start(self, ch, msg):
+
+    if self.client.mechanism is None:
+      if self.client.response is not None:
+        # Supports users passing the response argument alon
+        self.client.mechanism = "AMQPLAIN"
+      else:
+        supportedMechs = msg.frame.args[3].split()
+
+        self.client.sasl = get_sasl_mechanism(supportedMechs, self.client.username, self.client.password, sasl_options=self.client.sasl_options)
+
+        if self.client.sasl == None:
+          raise SaslException("sasl negotiation failed: no mechanism agreed.  Server supports: %s " % supportedMechs)
+
+        self.client.mechanism = self.client.sasl.mechanismName()
+
+    if self.client.response is None:
+      self.client.response = self.client.sasl.initialResponse()
+
     msg.start_ok(mechanism=self.client.mechanism,
-                 response=self.client.response,
+                 response=self.client.response or "",
                  locale=self.client.locale,
                  client_properties=self.client.client_properties)
 
+  def connection_secure(self, ch, msg):
+    msg.secure_ok(response=self.client.sasl.response(msg.challenge))
+
   def connection_tune(self, ch, msg):
     if self.client.tune_params:
       #todo: just override the params, i.e. don't require them

Added: qpid/trunk/qpid/python/qpid/saslmech/__init__.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/__init__.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/__init__.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/__init__.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""Pure python implementations of SASL mechanisms"""
+
+

Added: qpid/trunk/qpid/python/qpid/saslmech/amqplain.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/amqplain.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/amqplain.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/amqplain.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl, SaslException
+
+class AMQPLAIN(Sasl):
+
+  def initialResponse(self):
+    if (self.user is None or self.password is None):
+      raise SaslException("User and password must be specified")
+
+    return {"LOGIN": self.user, "PASSWORD": self.password}
+
+  def priority(self):
+    return 0
\ No newline at end of file

Added: qpid/trunk/qpid/python/qpid/saslmech/anonymous.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/anonymous.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/anonymous.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/anonymous.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl
+
+class ANONYMOUS(Sasl): pass
+

Added: qpid/trunk/qpid/python/qpid/saslmech/cram_md5.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/cram_md5.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/cram_md5.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/cram_md5.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl, SaslException
+from hmac import HMAC
+
+class CRAM_MD5(Sasl):
+
+  def response(self, challenge):
+    if (self.user is None or self.password is None):
+      raise SaslException("User and password must be specified")
+
+    digest = HMAC( self.password, challenge).hexdigest()
+    return "%s %s" % (self.user, digest)

Added: qpid/trunk/qpid/python/qpid/saslmech/cram_md5_hex.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/cram_md5_hex.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/cram_md5_hex.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/cram_md5_hex.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl
+from hmac import HMAC
+from hashlib import md5
+
+class CRAM_MD5_HEX(Sasl):
+
+  def response(self, challenge):
+    if (self.user is None or self.password is None):
+      raise SaslException("User and password must be specified")
+
+    m = md5()
+    m.update(self.password)
+    digest = HMAC( m.hexdigest(), challenge).hexdigest()
+    return "%s %s" % (self.user, digest)

Added: qpid/trunk/qpid/python/qpid/saslmech/external.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/external.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/external.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/external.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl
+
+
+class EXTERNAL(Sasl): pass
\ No newline at end of file

Added: qpid/trunk/qpid/python/qpid/saslmech/finder.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/finder.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/finder.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/finder.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+from logging import getLogger
+
+log = getLogger("qpid.saslmech")
+
+def get_sasl_mechanism(mechanismNames, username, password, namespace="qpid.saslmech", sasl_options=None):
+  """Given a list of SASL mechanism names, dynamically loads a SASL implementation
+     from namespace qpid.sasl.mech respecting a mechanism priority"""
+
+  log.debug("Supported mechanism : %s", mechanismNames)
+
+  instances = []
+  for mechanismName in mechanismNames:
+    convertedName = mechanismName.replace("-","_")
+    canonicalName = "%s.%s.%s" % (namespace, convertedName.lower(), convertedName)
+    try:
+      log.debug("Checking for SASL implementation %s for mechanism %s", canonicalName, mechanismName)
+      clazz = _get_class(canonicalName)
+      log.debug("Found SASL implementation")
+      instance = clazz(username, password, mechanismName, sasl_options)
+      instances.append(instance)
+    except (ImportError, AttributeError), e:
+      # Unknown mechanism - this is normal if the server supports m
+      log.debug("Could not load implementation for %s", canonicalName)
+      pass
+
+  if instances:
+    instances.sort(key=lambda x : x.priority(), reverse=True)
+    sasl = instances[0]
+    log.debug("Selected SASL mechanism %s", sasl.mechanismName())
+    return sasl
+  else:
+    return None
+
+def _get_class( kls ):
+  parts = kls.split('.')
+  module = ".".join(parts[:-1])
+  m = __import__( module )
+  for comp in parts[1:]:
+    m = getattr(m, comp)
+  return m
+
+
+

Added: qpid/trunk/qpid/python/qpid/saslmech/plain.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/plain.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/plain.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/plain.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from sasl import Sasl, SaslException
+
+class PLAIN(Sasl):
+
+  def initialResponse(self):
+    if (self.user is None or self.password is None):
+      raise SaslException("User and password must be specified")
+
+    return "\x00" + self.user + "\x00" + self.password
+
+  def priority(self):
+    return 0
\ No newline at end of file

Copied: qpid/trunk/qpid/python/qpid/saslmech/sasl.py (from r1627553, qpid/trunk/qpid/python/qpid/tests/__init__.py)
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/sasl.py?p2=qpid/trunk/qpid/python/qpid/saslmech/sasl.py&p1=qpid/trunk/qpid/python/qpid/tests/__init__.py&r1=1627553&r2=1627554&rev=1627554&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/__init__.py (original)
+++ qpid/trunk/qpid/python/qpid/saslmech/sasl.py Thu Sep 25 14:40:31 2014
@@ -6,9 +6,9 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-# 
+#
 #   http://www.apache.org/licenses/LICENSE-2.0
-# 
+#
 # Unless required by applicable law or agreed to in writing,
 # software distributed under the License is distributed on an
 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -17,45 +17,25 @@
 # under the License.
 #
 
-class Test:
+class SaslException(Exception): pass
+
+class Sasl:
 
-  def __init__(self, name):
+  def __init__(self, user, password, name, sasl_options = None):
+    self.user = user
+    self.password = password
     self.name = name
+    self.sasl_options = sasl_options
+
+  def initialResponse(self):
+    return
+
+  def response(self, challenge):
+    return
 
-  def configure(self, config):
-    self.config = config
+  def priority(self):
+    """Priority of the mechanism.  Mechanism with a higher value will be chosen in preference to those with a lower priority"""
+    return 1
 
-# API Tests
-import qpid.tests.framing
-import qpid.tests.mimetype
-import qpid.tests.messaging
-
-# Legacy Tests
-import qpid.tests.codec
-import qpid.tests.queue
-import qpid.tests.datatypes
-import qpid.tests.connection
-import qpid.tests.spec010
-import qpid.tests.codec010
-import qpid.tests.util
-
-class TestTestsXXX(Test):
-
-  def testFoo(self):
-    print "this test has output"
-
-  def testBar(self):
-    print "this test "*8
-    print "has"*10
-    print "a"*75
-    print "lot of"*10
-    print "output"*10
-
-  def testQux(self):
-    import sys
-    sys.stdout.write("this test has output with no newline")
-
-  def testQuxFail(self):
-    import sys
-    sys.stdout.write("this test has output with no newline")
-    fdsa
+  def mechanismName(self):
+    return self.name
\ No newline at end of file

Added: qpid/trunk/qpid/python/qpid/saslmech/scram.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/scram.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/scram.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/scram.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,94 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from hmac import HMAC
+from binascii import b2a_hex
+from sasl import Sasl
+import os
+import base64
+
+class SCRAM_base(Sasl):
+
+  def __init__(self, algorithm, user, password, name, sasl_options=None):
+    Sasl.__init__(self, user, password, name, sasl_options)
+    self.algorithm = algorithm
+    self.client_nonce = b2a_hex(os.urandom(16))
+    self.server_signature = None
+
+  def initialResponse(self):
+    if (self.user is None or self.password is None):
+      raise SaslException("User and password must be specified")
+
+    name = self.user.replace("=","=3D").replace(",","=2C")
+    self.client_first_message = "n=" + name + ",r=" + self.client_nonce
+    return "n,," + self.client_first_message
+
+
+  def response(self, challenge):
+    if(self.server_signature):
+      self.evaluateOutcome(challenge)
+      return ""
+    else:
+      serverChallenge, salt, iterations = challenge.split(",")
+      self.server_nonce = serverChallenge[2:]
+      if self.server_nonce.find(self.client_nonce) != 0:
+        raise SaslException("Server nonce does not start with client nonce")
+      self.salt = base64.b64decode(salt[2:])
+
+      iterations = int(iterations[2:])
+
+      hmac = HMAC(key=self.password.replace("=","=3D").replace(",","=2C"),digestmod=self.algorithm)
+
+      hmac.update(self.salt)
+      hmac.update("\x00\x00\x00\x01")
+
+      saltedPassword = hmac.digest()
+      previous = saltedPassword
+
+      for i in range(1,iterations):
+        hmac = HMAC(key=self.password.replace("=","=3D").replace(",","=2C"),digestmod=self.algorithm)
+        hmac.update(previous)
+        previous = hmac.digest()
+        saltedPassword = ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(saltedPassword,previous))
+
+      clientFinalMessageWithoutProof = "c=" + base64.b64encode("n,,") + ",r=" + self.server_nonce
+      authMessage = self.client_first_message + "," + challenge + "," + clientFinalMessageWithoutProof
+
+      clientKey = HMAC(key=saltedPassword,msg="Client Key",digestmod=self.algorithm).digest()
+      hashFunc = self.algorithm()
+      hashFunc.update(clientKey)
+      storedKey = hashFunc.digest()
+
+      clientSignature = HMAC(key=storedKey, msg=authMessage, digestmod=self.algorithm).digest()
+
+      clientProof = ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(clientKey,clientSignature))
+
+      serverKey = HMAC(key=saltedPassword,msg="Server Key",digestmod=self.algorithm).digest()
+
+      self.server_signature = HMAC(key=serverKey,msg=authMessage,digestmod=self.algorithm).digest()
+      return clientFinalMessageWithoutProof + ",p=" + base64.b64encode(clientProof)
+
+
+  def evaluateOutcome(self, challenge):
+    serverVerification = challenge.split(",")[0]
+
+    serverSignature = base64.b64decode(serverVerification[2:])
+    if serverSignature != self.server_signature:
+      raise SaslException("Server verification failed")
+    return

Added: qpid/trunk/qpid/python/qpid/saslmech/scram_sha_1.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/scram_sha_1.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/scram_sha_1.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/scram_sha_1.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from scram import SCRAM_base
+from hashlib import sha1
+
+class SCRAM_SHA_1(SCRAM_base):
+
+  def __init__(self, user, password, name, sasl_options=None):
+    SCRAM_base.__init__(self, sha1, user, password, name, sasl_options)
+

Added: qpid/trunk/qpid/python/qpid/saslmech/scram_sha_256.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/saslmech/scram_sha_256.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/saslmech/scram_sha_256.py (added)
+++ qpid/trunk/qpid/python/qpid/saslmech/scram_sha_256.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from scram import SCRAM_base
+import hashlib
+
+class SCRAM_SHA_256(SCRAM_base):
+  def __init__(self, user, password, name, sasl_options=None):
+    SCRAM_base.__init__(self,hashlib.sha256,user,password, name, sasl_options)
\ No newline at end of file

Modified: qpid/trunk/qpid/python/qpid/testlib.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/testlib.py?rev=1627554&r1=1627553&r2=1627554&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/testlib.py (original)
+++ qpid/trunk/qpid/python/qpid/testlib.py Thu Sep 25 14:40:31 2014
@@ -84,10 +84,7 @@ class TestBase(unittest.TestCase):
         password = password or self.config.broker.password or "guest"
         client = qpid.client.Client(host, port)
         try:
-            if client.spec.major == 8 and client.spec.minor == 0:
-                client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params, client_properties=client_properties)
-            else:
-                client.start("\x00" + user + "\x00" + password, mechanism="PLAIN", tune_params=tune_params, client_properties=client_properties)
+          client.start(username = user, password=password, tune_params=tune_params, client_properties=client_properties)
         except qpid.client.Closed, e:
             if isinstance(e.args[0], VersionError):
                 raise Skipped(e.args[0])

Modified: qpid/trunk/qpid/python/qpid/tests/__init__.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/__init__.py?rev=1627554&r1=1627553&r2=1627554&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/__init__.py (original)
+++ qpid/trunk/qpid/python/qpid/tests/__init__.py Thu Sep 25 14:40:31 2014
@@ -38,6 +38,7 @@ import qpid.tests.connection
 import qpid.tests.spec010
 import qpid.tests.codec010
 import qpid.tests.util
+import qpid.tests.saslmech.finder
 
 class TestTestsXXX(Test):
 

Added: qpid/trunk/qpid/python/qpid/tests/saslmech/__init__.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/saslmech/__init__.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/saslmech/__init__.py (added)
+++ qpid/trunk/qpid/python/qpid/tests/saslmech/__init__.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+

Added: qpid/trunk/qpid/python/qpid/tests/saslmech/finder.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/saslmech/finder.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/saslmech/finder.py (added)
+++ qpid/trunk/qpid/python/qpid/tests/saslmech/finder.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,62 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from unittest import TestCase
+from qpid.saslmech.finder import get_sasl_mechanism
+from my_sasl import MY_SASL
+from my_sasl2 import MY_SASL2
+
+class SaslFinderTests (TestCase):
+  """Tests the ability to chose the a sasl mechanism from those available to be loaded"""
+
+  def test_known_mechansim(self):
+
+    supportedMechs = ["MY-SASL"]
+
+    mech = get_sasl_mechanism(supportedMechs, "myuser", "mypass", namespace="qpid.tests.saslmech")
+
+    self.assertTrue(isinstance(mech, MY_SASL), "Mechanism %s is of unexpected type" % mech)
+    self.assertEquals("MY-SASL", mech.mechanismName())
+    self.assertTrue(mech.sasl_options is None)
+
+  def test_unknown_mechansim(self):
+
+    supportedMechs = ["not_a_mech"]
+
+    mech = get_sasl_mechanism(supportedMechs, "myuser", "mypass", namespace="qpid.tests.saslmech")
+
+    self.assertTrue(mech == None, "Mechanism instance should be none")
+
+  def test_sasl_mechanism_with_higher_priority_prefered(self):
+
+    supportedMechs = ["MY-SASL", "MY-SASL2"]
+
+    mech = get_sasl_mechanism(supportedMechs, "myuser", "mypass", namespace="qpid.tests.saslmech")
+
+    self.assertTrue(isinstance(mech, MY_SASL), "Mechanism %s is of unexpected type" % mech)
+
+  def test_sasl_mechansim_options(self):
+
+    supportedMechs = ["MY-SASL"]
+
+    sasl_options = {'hello': 'world'}
+    mech = get_sasl_mechanism(supportedMechs, "myuser", "mypass", namespace="qpid.tests.saslmech", sasl_options=sasl_options)
+
+    self.assertTrue(isinstance(mech, MY_SASL), "Mechanism %s is of unexpected type" % mech)
+    self.assertEquals(sasl_options, mech.sasl_options)

Added: qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl.py (added)
+++ qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from qpid.saslmech.sasl import Sasl
+
+class MY_SASL(Sasl): pass

Added: qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl2.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl2.py?rev=1627554&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl2.py (added)
+++ qpid/trunk/qpid/python/qpid/tests/saslmech/my_sasl2.py Thu Sep 25 14:40:31 2014
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from qpid.saslmech.sasl import Sasl
+
+class MY_SASL2(Sasl):
+
+  def priority(self):
+    return 0
\ No newline at end of file

Modified: qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/echo.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/echo.py?rev=1627554&r1=1627553&r2=1627554&view=diff
==============================================================================
--- qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/echo.py (original)
+++ qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/echo.py Thu Sep 25 14:40:31 2014
@@ -90,7 +90,7 @@ class EchoTests(TestBase):
         # the content in order to send it to us.
         consuming_client = qpid.client.Client(self.config.broker.host, self.config.broker.port)
         tune_params = { "channel_max" : 256, "frame_max" : 4096 }
-        consuming_client.start("\x00" + self.config.broker.user + "\x00" + self.config.broker.password, mechanism="PLAIN", tune_params = tune_params)
+        consuming_client.start(username = self.config.broker.user, password = self.config.broker.password, tune_params = tune_params)
 
         consuming_channel = consuming_client.channel(1)
         consuming_channel.channel_open()



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org