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