Rev 4285: (Jelmer, vila) Support registration of fallback credential stores. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Sat Apr 11 14:01:23 BST 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4285
revision-id: pqm at pqm.ubuntu.com-20090411130119-4kn8b6070uyqg5xx
parent: pqm at pqm.ubuntu.com-20090410193720-nyej7ft1k2yoyhui
parent: jelmer at samba.org-20090411120858-12ttazj3z13lxaza
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Sat 2009-04-11 14:01:19 +0100
message:
  (Jelmer, vila) Support registration of fallback credential stores.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
  bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
    ------------------------------------------------------------
    revno: 4283.1.4
    revision-id: jelmer at samba.org-20090411120858-12ttazj3z13lxaza
    parent: jelmer at samba.org-20090410211207-l8nvhg48ltr15s1h
    parent: v.ladeuil+lp at free.fr-20090411091328-dua3x32nla71mjwq
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: fallback
    timestamp: Sat 2009-04-11 14:08:58 +0200
    message:
      Merge tweaks from Vincent.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
        ------------------------------------------------------------
        revno: 4283.2.1
        revision-id: v.ladeuil+lp at free.fr-20090411091328-dua3x32nla71mjwq
        parent: jelmer at samba.org-20090410211207-l8nvhg48ltr15s1h
        committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
        branch nick: 256612-http-auth
        timestamp: Sat 2009-04-11 11:13:28 +0200
        message:
          Add a test and cleanup some PEP8 issues.
          
          * bzrlib/tests/test_config.py:
          PEP8, lines too long, trailing spaces.
          (TestCredentialStoreRegistry.test_fallback_first_wins): Check that
          first cs to provide credentials wins.
          
          * bzrlib/config.py:
          PEP8 and cosmetic changes.
        modified:
          bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
          bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
    ------------------------------------------------------------
    revno: 4283.1.3
    revision-id: jelmer at samba.org-20090410211207-l8nvhg48ltr15s1h
    parent: jelmer at samba.org-20090410205718-tte8d4zm5iqs02wt
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: fallback
    timestamp: Fri 2009-04-10 23:12:07 +0200
    message:
      Add test to make sure AuthenticationConfig queries for fallback credentials.
    modified:
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
    ------------------------------------------------------------
    revno: 4283.1.2
    revision-id: jelmer at samba.org-20090410205718-tte8d4zm5iqs02wt
    parent: jelmer at samba.org-20090410195421-0yof3ixspjdj8ckd
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: fallback
    timestamp: Fri 2009-04-10 22:57:18 +0200
    message:
      Add tests, NEWS item.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
    ------------------------------------------------------------
    revno: 4283.1.1
    revision-id: jelmer at samba.org-20090410195421-0yof3ixspjdj8ckd
    parent: pqm at pqm.ubuntu.com-20090410130227-fh2zl04tu0oq6cg6
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: fallback
    timestamp: Fri 2009-04-10 21:54:21 +0200
    message:
      Support fallback credential stores.
    modified:
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
=== modified file 'NEWS'
--- a/NEWS	2009-04-10 19:37:20 +0000
+++ b/NEWS	2009-04-11 13:01:19 +0000
@@ -54,6 +54,11 @@
   called before the first test is started, ``done`` called after the last
   test completes, and a new parameter ``strict``. (Robert Collins)
 
+* Fallback ``CredentialStore`` instances registered with ``fallback=True``
+  are now be able to provide credentials if obtaining credentials 
+  via ~/.bazaar/authentication.conf fails. (Jelmer Vernooij, 
+  Vincent Ladeuil, #321918)
+
 bzr 1.14rc1
 ###########
 :Codename: brisbane-core

=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2009-04-07 14:03:52 +0000
+++ b/bzrlib/config.py	2009-04-11 13:01:19 +0000
@@ -1079,6 +1079,12 @@
                 trace.mutter("Using authentication section: %r", auth_def_name)
             break
 
+        if credentials is None:
+            # No credentials were found in authentication.conf, try the fallback
+            # credentials stores.
+            credentials = credential_store_registry.get_fallback_credentials(
+                scheme, host, port, user, path, realm)
+
         return credentials
 
     def set_credentials(self, name, host, user, scheme=None, password=None,
@@ -1227,10 +1233,13 @@
     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
+    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.
+    fallback=fallback) to install themselves.
+
+    A fallback credential store is one that is queried if no credentials can be
+    found via authentication.conf.
     """
 
     def get_credential_store(self, encoding=None):
@@ -1239,6 +1248,68 @@
             cs = cs()
         return cs
 
+    def is_fallback(self, name):
+        """Check if the named credentials store should be used as fallback."""
+        return self.get_info(name)
+
+    def get_fallback_credentials(self, scheme, host, port=None, user=None,
+                                 path=None, realm=None):
+        """Request credentials from all fallback credentials stores.
+
+        The first credentials store that can provide credentials wins.
+        """
+        credentials = None
+        for name in self.keys():
+            if not self.is_fallback(name):
+                continue
+            cs = self.get_credential_store(name)
+            credentials = cs.get_credentials(scheme, host, port, user,
+                                             path, realm)
+            if credentials is not None:
+                # We found some credentials
+                break
+        return credentials
+
+    def register(self, key, obj, help=None, override_existing=False,
+                 fallback=False):
+        """Register a new object to a name.
+
+        :param key: This is the key to use to request the object later.
+        :param obj: The object to register.
+        :param help: Help text for this entry. This may be a string or
+                a callable. If it is a callable, it should take two
+                parameters (registry, key): this registry and the key that
+                the help was registered under.
+        :param override_existing: Raise KeyErorr if False and something has
+                already been registered for that key. If True, ignore if there
+                is an existing key (always register the new value).
+        :param fallback: Whether this credential store should be 
+                used as fallback.
+        """
+        return super(CredentialStoreRegistry,
+                     self).register(key, obj, help, info=fallback,
+                                    override_existing=override_existing)
+
+    def register_lazy(self, key, module_name, member_name,
+                      help=None, override_existing=False,
+                      fallback=False):
+        """Register a new credential store to be loaded on request.
+
+        :param module_name: The python path to the module. Such as 'os.path'.
+        :param member_name: The member of the module to return.  If empty or
+                None, get() will return the module itself.
+        :param help: Help text for this entry. This may be a string or
+                a callable.
+        :param override_existing: If True, replace the existing object
+                with the new one. If False, if there is already something
+                registered with the same key, raise a KeyError
+        :param fallback: Whether this credential store should be 
+                used as fallback.
+        """
+        return super(CredentialStoreRegistry, self).register_lazy(
+            key, module_name, member_name, help,
+            info=fallback, override_existing=override_existing)
+
 
 credential_store_registry = CredentialStoreRegistry()
 
@@ -1247,9 +1318,18 @@
     """An abstract class to implement storage for credentials"""
 
     def decode_password(self, credentials):
-        """Returns a password for the provided credentials in clear text."""
+        """Returns a clear text password for the provided credentials."""
         raise NotImplementedError(self.decode_password)
 
+    def get_credentials(self, scheme, host, port=None, user=None, path=None,
+                        realm=None):
+        """Return the matching credentials from this credential store.
+
+        This method is only called on fallback credential stores.
+        """
+        raise NotImplementedError(self.get_credentials)
+
+
 
 class PlainTextCredentialStore(CredentialStore):
     """Plain text credential store for the authentication.conf file."""

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2009-04-07 15:28:22 +0000
+++ b/bzrlib/tests/test_config.py	2009-04-11 13:01:19 +0000
@@ -1576,6 +1576,50 @@
             self._get_log(keep_log_file=True),
             'password ignored in section \[ssh with password\]')
 
+    def test_uses_fallback_stores(self):
+        self._old_cs_registry = config.credential_store_registry
+        def restore():
+            config.credential_store_registry = self._old_cs_registry
+        self.addCleanup(restore)
+        config.credential_store_registry = config.CredentialStoreRegistry()
+        store = StubCredentialStore()
+        store.add_credentials("http", "example.com", "joe", "secret")
+        config.credential_store_registry.register("stub", store, fallback=True)
+        conf = config.AuthenticationConfig(_file=StringIO())
+        creds = conf.get_credentials("http", "example.com")
+        self.assertEquals("joe", creds["user"])
+        self.assertEquals("secret", creds["password"])
+
+
+class StubCredentialStore(config.CredentialStore):
+
+    def __init__(self):
+        self._username = {}
+        self._password = {}
+
+    def add_credentials(self, scheme, host, user, password=None):
+        self._username[(scheme, host)] = user
+        self._password[(scheme, host)] = password
+
+    def get_credentials(self, scheme, host, port=None, user=None,
+        path=None, realm=None):
+        key = (scheme, host)
+        if not key in self._username:
+            return None
+        return { "scheme": scheme, "host": host, "port": port,
+                "user": self._username[key], "password": self._password[key]}
+
+
+class CountingCredentialStore(config.CredentialStore):
+
+    def __init__(self):
+        self._calls = 0
+
+    def get_credentials(self, scheme, host, port=None, user=None,
+        path=None, realm=None):
+        self._calls += 1
+        return None
+
 
 class TestCredentialStoreRegistry(tests.TestCase):
 
@@ -1593,6 +1637,64 @@
         # 'unknown' so we use that as an never registered key.
         self.assertRaises(KeyError, r.get_credential_store, 'unknown')
 
+    def test_fallback_none_registered(self):
+        r = config.CredentialStoreRegistry()
+        self.assertEquals(None,
+                          r.get_fallback_credentials("http", "example.com"))
+
+    def test_register(self):
+        r = config.CredentialStoreRegistry()
+        r.register("stub", StubCredentialStore(), fallback=False)
+        r.register("another", StubCredentialStore(), fallback=True)
+        self.assertEquals(["another", "stub"], r.keys())
+
+    def test_register_lazy(self):
+        r = config.CredentialStoreRegistry()
+        r.register_lazy("stub", "bzrlib.tests.test_config",
+                        "StubCredentialStore", fallback=False)
+        self.assertEquals(["stub"], r.keys())
+        self.assertIsInstance(r.get_credential_store("stub"),
+                              StubCredentialStore)
+
+    def test_is_fallback(self):
+        r = config.CredentialStoreRegistry()
+        r.register("stub1", None, fallback=False)
+        r.register("stub2", None, fallback=True)
+        self.assertEquals(False, r.is_fallback("stub1"))
+        self.assertEquals(True, r.is_fallback("stub2"))
+
+    def test_no_fallback(self):
+        r = config.CredentialStoreRegistry()
+        store = CountingCredentialStore()
+        r.register("count", store, fallback=False)
+        self.assertEquals(None,
+                          r.get_fallback_credentials("http", "example.com"))
+        self.assertEquals(0, store._calls)
+
+    def test_fallback_credentials(self):
+        r = config.CredentialStoreRegistry()
+        store = StubCredentialStore()
+        store.add_credentials("http", "example.com",
+                              "somebody", "geheim")
+        r.register("stub", store, fallback=True)
+        creds = r.get_fallback_credentials("http", "example.com")
+        self.assertEquals("somebody", creds["user"])
+        self.assertEquals("geheim", creds["password"])
+
+    def test_fallback_first_wins(self):
+        r = config.CredentialStoreRegistry()
+        stub1 = StubCredentialStore()
+        stub1.add_credentials("http", "example.com",
+                              "somebody", "stub1")
+        r.register("stub1", stub1, fallback=True)
+        stub2 = StubCredentialStore()
+        stub2.add_credentials("http", "example.com",
+                              "somebody", "stub2")
+        r.register("stub2", stub1, fallback=True)
+        creds = r.get_fallback_credentials("http", "example.com")
+        self.assertEquals("somebody", creds["user"])
+        self.assertEquals("stub1", creds["password"])
+
 
 class TestPlainTextCredentialStore(tests.TestCase):
 




More information about the bazaar-commits mailing list