Rev 3758: Add credential stores plugging. in file:///v/home/vila/src/bzr/experimental/pluggable-auth-conf/

Vincent Ladeuil v.ladeuil+lp at free.fr
Sun Oct 5 22:32:01 BST 2008


At file:///v/home/vila/src/bzr/experimental/pluggable-auth-conf/

------------------------------------------------------------
revno: 3758
revision-id: v.ladeuil+lp at free.fr-20081005213200-tbgbnnhq6rjabnlz
parent: pqm at pqm.ubuntu.com-20081001123103-9powbklax4nmw09j
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: pluggable-auth-conf
timestamp: Sun 2008-10-05 23:32:00 +0200
message:
  Add credential stores plugging.
  
  * tests/test_config.py:
  (TestAuthenticationConfigFile.test_unknown_password_encoding):
  Test that the user get a meaningful error on typos or installation
  problems.
  (TestCredentialStoreRegistry): Basic tests for the registry.
  (TestPlainTextCredentialStore): Test plain text credential store.
  
  * config.py:
  (AuthenticationConfig.decode_password): Go through the credential
  store registry to decode the password.
  (CredentialStoreRegistry): New registry for credential stores.
  (CredentialStore): Abstract base class for credential stores.
  (PlainTextCredentialStore): Default, plain text, credential store,
  using authentication.conf file itself as storage(i.e actual
  behavior).
-------------- next part --------------
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2008-09-08 12:59:00 +0000
+++ b/bzrlib/config.py	2008-10-05 21:32:00 +0000
@@ -78,6 +78,7 @@
     errors,
     mail_client,
     osutils,
+    registry,
     symbol_versioning,
     trace,
     ui,
@@ -1131,9 +1132,57 @@
         return password
 
     def decode_password(self, credentials, encoding):
+        try:
+            cs = credential_store_registry.get_credential_store(encoding)
+        except KeyError:
+            raise ValueError('%r is not a known password_encoding' % encoding)
+        credentials['password'] = cs.decode_password(credentials)
         return credentials
 
 
+class CredentialStoreRegistry(registry.Registry):
+    """A class that registers credential stores.
+
+    A credential store provides access to credentials via the password_encoding
+    field in authentication.conf sections.
+
+    Except for stores provided by bzr itself,most stores are expected to be
+    provided by plugins that will therefore use
+    register_lazy(password_encoding, module_name, member_name, help=help,
+    info=info) to install themselves.
+    """
+
+    def get_credential_store(self, encoding=None):
+        cs = self.get(encoding)
+        if callable(cs):
+            cs = cs()
+        return cs
+
+
+credential_store_registry = CredentialStoreRegistry()
+
+
+class CredentialStore(object):
+    """An abstract class to implement storage for credentials"""
+
+    def decode_password(self, credentials):
+        """Returns a password for the provided credentials in clear text."""
+        raise NotImplementedError(self.decode_password)
+
+
+class PlainTextCredentialStore(CredentialStore):
+    """Plain text credential store for the authentication.conf file."""
+
+    def decode_password(self, credentials):
+        """See CredentialStore.decode_password."""
+        return credentials['password']
+
+
+credential_store_registry.register('plain', PlainTextCredentialStore,
+                                   help=PlainTextCredentialStore.__doc__)
+credential_store_registry.default_key = 'plain'
+
+
 class BzrDirConfig(object):
 
     def __init__(self, transport):

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2008-08-10 11:26:50 +0000
+++ b/bzrlib/tests/test_config.py	2008-10-05 21:32:00 +0000
@@ -1263,6 +1263,16 @@
 """))
         self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
+    def test_unknown_password_encoding(self):
+        conf = config.AuthenticationConfig(_file=StringIO(
+                """[broken]
+scheme=ftp
+user=joe
+password_encoding=unknown
+"""))
+        self.assertRaises(ValueError, conf.get_password,
+                          'ftp', 'foo.net', 'joe')
+
     def test_credentials_for_scheme_host(self):
         conf = config.AuthenticationConfig(_file=StringIO(
                 """# Identity on foo.net
@@ -1500,6 +1510,32 @@
             'password ignored in section \[ssh with password\]')
 
 
+class TestCredentialStoreRegistry(tests.TestCase):
+
+    def _get_cs_registry(self):
+        return config.credential_store_registry
+
+    def test_default_credential_store(self):
+        r = self._get_cs_registry()
+        default = r.get_credential_store(None)
+        self.assertIsInstance(default, config.PlainTextCredentialStore)
+
+    def test_unknown_credential_store(self):
+        r = self._get_cs_registry()
+        # It's hard to imagine someone creating a credential store named
+        # 'unknown' so we use that as an never registered key.
+        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
+
+
+class TestPlainTextCredentialStore(tests.TestCase):
+
+    def test_decode_password(self):
+        r = config.credential_store_registry
+        plain_text = r.get_credential_store()
+        decoded = plain_text.decode_password(dict(password='secret'))
+        self.assertEquals('secret', decoded)
+
+
 # FIXME: Once we have a way to declare authentication to all test servers, we
 # can implement generic tests.
 # test_user_password_in_url



More information about the bazaar-commits mailing list