[Repoze-checkins] r729 - in repoze.pam/trunk: . repoze/pam repoze/pam/fixtures repoze/pam/plugins

Chris McDonough chrism at agendaless.com
Sat Feb 23 23:36:53 UTC 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Sat Feb 23 18:36:53 2008
New Revision: 729

Log:
Add htpasswd authenticator plugin.


Added:
   repoze.pam/trunk/repoze/pam/fixtures/
   repoze.pam/trunk/repoze/pam/fixtures/test.htpasswd
   repoze.pam/trunk/repoze/pam/plugins/htpasswd.py   (contents, props changed)
   repoze.pam/trunk/repoze/pam/utils.py   (contents, props changed)
Modified:
   repoze.pam/trunk/repoze/pam/tests.py
   repoze.pam/trunk/setup.py

Added: repoze.pam/trunk/repoze/pam/fixtures/test.htpasswd
==============================================================================
--- (empty file)
+++ repoze.pam/trunk/repoze/pam/fixtures/test.htpasswd	Sat Feb 23 18:36:53 2008
@@ -0,0 +1,2 @@
+badline
+chrism:pass

Added: repoze.pam/trunk/repoze/pam/plugins/htpasswd.py
==============================================================================
--- (empty file)
+++ repoze.pam/trunk/repoze/pam/plugins/htpasswd.py	Sat Feb 23 18:36:53 2008
@@ -0,0 +1,50 @@
+from zope.interface import implements
+
+from repoze.pam.interfaces import IAuthenticatorPlugin
+from repoze.pam.utils import resolveDotted
+
+class HTPasswdAuthenticator(object):
+
+    implements(IAuthenticatorPlugin)
+
+    def __init__(self, filename, check):
+        self.filename = filename
+        self.check = check
+
+    # IAuthenticatorPlugin
+    def authenticate(self, environ, credentials):
+        try:
+            login = credentials['login']
+            password = credentials['password']
+        except KeyError:
+            return False
+
+        if hasattr(self.filename, 'seek'):
+            # assumed to have a readline
+            self.filename.seek(0)
+            f = self.filename
+        else:
+            try:
+                f = open(self.filename, 'r')
+            except IOError:
+                return False
+
+        for line in f:
+            try:
+                username, hashed = line.rstrip().split(':', 1)
+            except ValueError:
+                continue
+            if username == login:
+                return self.check(password, hashed)
+        return False
+
+def check_crypted(password, hashed):
+    from crypt import crypt
+    salt = hashed[:2]
+    return hashed == crypt(password, salt)
+
+def make_plugin(pam_conf, filename, check_fn):
+    check = resolveDotted(check_fn)
+    return HTPasswdAuthenticator(filename, check)
+
+    

Modified: repoze.pam/trunk/repoze/pam/tests.py
==============================================================================
--- repoze.pam/trunk/repoze/pam/tests.py	(original)
+++ repoze.pam/trunk/repoze/pam/tests.py	Sat Feb 23 18:36:53 2008
@@ -1,6 +1,17 @@
+import os
 import unittest
 
-class TestBasicAuthPlugin(unittest.TestCase):
+here = os.path.abspath(os.path.dirname(__file__))
+
+class Base(unittest.TestCase):
+    def _makeEnviron(self, kw=None):
+        environ = {}
+        environ['wsgi.version'] = (1,0)
+        if kw is not None:
+            environ.update(kw)
+        return environ
+
+class TestBasicAuthPlugin(Base):
     def _getTargetClass(self):
         from repoze.pam.plugins.basicauth import BasicAuthPlugin
         return BasicAuthPlugin
@@ -9,13 +20,6 @@
         plugin = self._getTargetClass()(*arg, **kw)
         return plugin
 
-    def _makeEnviron(self, kw=None):
-        environ = {}
-        environ['wsgi.version'] = (1,0)
-        if kw is not None:
-            environ.update(kw)
-        return environ
-    
     def test_implements(self):
         from zope.interface.verify import verifyClass
         from repoze.pam.interfaces import IChallengerPlugin
@@ -74,3 +78,93 @@
         self.assertEqual(plugin.realm, 'realm')
         self.assertEqual(plugin.requests, ['a', 'b'])
         
+class TestHTPasswdAuthenticator(Base):
+    def _getTargetClass(self):
+        from repoze.pam.plugins.htpasswd import HTPasswdAuthenticator
+        return HTPasswdAuthenticator
+
+    def _makeOne(self, *arg, **kw):
+        plugin = self._getTargetClass()(*arg, **kw)
+        return plugin
+
+    def test_implements(self):
+        from zope.interface.verify import verifyClass
+        from repoze.pam.interfaces import IAuthenticatorPlugin
+        klass = self._getTargetClass()
+        verifyClass(IAuthenticatorPlugin, klass)
+
+    def test_authenticate_nocreds(self):
+        from StringIO import StringIO
+        io = StringIO()
+        plugin = self._makeOne(io, None)
+        environ = self._makeEnviron()
+        creds = {}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, False)
+        
+    def test_authenticate_nolines(self):
+        from StringIO import StringIO
+        io = StringIO()
+        plugin = self._makeOne(io, None)
+        environ = self._makeEnviron()
+        creds = {'login':'chrism', 'password':'pass'}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, False)
+        
+    def test_authenticate_nousermatch(self):
+        from StringIO import StringIO
+        io = StringIO('nobody:foo')
+        plugin = self._makeOne(io, None)
+        environ = self._makeEnviron()
+        creds = {'login':'chrism', 'password':'pass'}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, False)
+
+    def test_authenticate_match(self):
+        from StringIO import StringIO
+        io = StringIO('chrism:pass')
+        def check(password, hashed):
+            return True
+        plugin = self._makeOne(io, check)
+        environ = self._makeEnviron()
+        creds = {'login':'chrism', 'password':'pass'}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, True)
+
+    def test_authenticate_badline(self):
+        from StringIO import StringIO
+        io = StringIO('badline\nchrism:pass')
+        def check(password, hashed):
+            return True
+        plugin = self._makeOne(io, check)
+        environ = self._makeEnviron()
+        creds = {'login':'chrism', 'password':'pass'}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, True)
+
+    def test_authenticate_filename(self):
+        htpasswd = os.path.join(here, 'fixtures', 'test.htpasswd')
+        def check(password, hashed):
+            return True
+        plugin = self._makeOne(htpasswd, check)
+        environ = self._makeEnviron()
+        creds = {'login':'chrism', 'password':'pass'}
+        result = plugin.authenticate(environ, creds)
+        self.assertEqual(result, True)
+
+    def test_check_crypted(self):
+        from crypt import crypt
+        salt = '123'
+        hashed = crypt('password', salt)
+        from repoze.pam.plugins.htpasswd import check_crypted
+        self.assertEqual(check_crypted('password', hashed), True)
+        self.assertEqual(check_crypted('notpassword', hashed), False)
+
+    def test_factory(self):
+        from repoze.pam.plugins.htpasswd import make_plugin
+        from repoze.pam.plugins.htpasswd import check_crypted
+        plugin = make_plugin({}, 'foo',
+                             'repoze.pam.plugins.htpasswd:check_crypted')
+        self.assertEqual(plugin.filename, 'foo')
+        self.assertEqual(plugin.check, check_crypted)
+        

Added: repoze.pam/trunk/repoze/pam/utils.py
==============================================================================
--- (empty file)
+++ repoze.pam/trunk/repoze/pam/utils.py	Sat Feb 23 18:36:53 2008
@@ -0,0 +1,5 @@
+def resolveDotted(dotted_or_ep):
+    """ Resolve a dotted name or setuptools entry point to a callable.
+    """
+    from pkg_resources import EntryPoint
+    return EntryPoint.parse('x=%s' % dotted_or_ep).load(False)

Modified: repoze.pam/trunk/setup.py
==============================================================================
--- repoze.pam/trunk/setup.py	(original)
+++ repoze.pam/trunk/setup.py	Sat Feb 23 18:36:53 2008
@@ -50,8 +50,8 @@
       include_package_data=True,
       namespace_packages=['repoze'],
       zip_safe=False,
-      tests_require = [],
-      install_requires=[],
+      tests_require = ['Paste', 'zope.interface'],
+      install_requires=['Paste', 'zope.interface'],
       test_suite="repoze.pam.tests",
       entry_points = """\
       [paste.filter_app_factory]


More information about the Repoze-checkins mailing list