mirror of
https://github.com/openSUSE/osc.git
synced 2025-01-24 22:06:14 +01:00
Merge commit 'refs/pull/1022/head' of github.com:openSUSE/osc
Only ask for a password if it is really needed for authentication. The new lazy password approach is much smarter than the old callable hack. That's why we deprecate returning a callable from AbstractCredentialsManager.get_password. The current compatibility code for a callable will be removed in the near future. Minor nitpick: actually it would have been "cleaner" to introduce a new subclass like an AbstractLazyPasswordCredentialsManager that encapsulates the lazy password behavior. Currently, if, for instance, a credentials manager is always non-lazy it would just override get_password but still inherits the abstract (and unused) _get_password method.
This commit is contained in:
commit
90ccc84f95
@ -765,7 +765,7 @@ def config_set_option(section, opt, val=None, delete=False, update=True, creds_m
|
||||
# change password store
|
||||
creds_mgr = _get_credentials_manager(section, cp)
|
||||
user = _extract_user_compat(cp, section, creds_mgr)
|
||||
val = creds_mgr.get_password(section, user)
|
||||
val = creds_mgr.get_password(section, user, defer=False)
|
||||
|
||||
run = False
|
||||
if val:
|
||||
@ -885,6 +885,8 @@ class APIHostOptionsEntry(dict):
|
||||
def __getitem__(self, key, *args, **kwargs):
|
||||
value = super(self.__class__, self).__getitem__(key, *args, **kwargs)
|
||||
if key == 'pass' and callable(value):
|
||||
print('Warning: use of a deprecated credentials manager API.',
|
||||
file=sys.stderr)
|
||||
value = value()
|
||||
return value
|
||||
|
||||
@ -980,7 +982,7 @@ def get_config(override_conffile=None,
|
||||
user = _extract_user_compat(cp, url, creds_mgr)
|
||||
if user is None:
|
||||
raise oscerr.ConfigMissingCredentialsError('No user found in section %s' % url, conffile, url)
|
||||
password = creds_mgr.get_password(url, user)
|
||||
password = creds_mgr.get_password(url, user, defer=True)
|
||||
if password is None:
|
||||
raise oscerr.ConfigMissingCredentialsError('No password found in section %s' % url, conffile, url)
|
||||
|
||||
|
@ -33,6 +33,31 @@ from . import conf
|
||||
from . import oscerr
|
||||
|
||||
|
||||
class _LazyPassword(object):
|
||||
def __init__(self, pwfunc):
|
||||
self._pwfunc = pwfunc
|
||||
self._password = None
|
||||
|
||||
def __str__(self):
|
||||
if self._password is None:
|
||||
self._password = self._pwfunc()
|
||||
if self._password is None:
|
||||
raise oscerr.OscIOError(None, 'Unable to retrieve password')
|
||||
return self._password
|
||||
|
||||
def __len__(self):
|
||||
return len(str(self))
|
||||
|
||||
def __add__(self, other):
|
||||
return str(self) + other
|
||||
|
||||
def __radd__(self, other):
|
||||
return other + str(self)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(str(self), name)
|
||||
|
||||
|
||||
class AbstractCredentialsManagerDescriptor(object):
|
||||
def name(self):
|
||||
raise NotImplementedError()
|
||||
@ -64,14 +89,15 @@ class AbstractCredentialsManager(object):
|
||||
def create(cls, cp, options):
|
||||
return cls(cp, options)
|
||||
|
||||
def get_password(self, url, user, defer=True):
|
||||
# If defer is True a callable can be returned
|
||||
# and the password is retrieved if the callable
|
||||
# is called. Implementations are free to ignore
|
||||
# defer parameter and can directly return the password.
|
||||
# If defer is False the password is directly returned.
|
||||
def _get_password(self, url, user):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_password(self, url, user, defer=True):
|
||||
if defer:
|
||||
return _LazyPassword(lambda: self._get_password(url, user))
|
||||
else:
|
||||
return self._get_password(url, user)
|
||||
|
||||
def set_password(self, url, user, password):
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -162,10 +188,10 @@ class TransientCredentialsManager(AbstractCredentialsManager):
|
||||
if options is not None:
|
||||
raise RuntimeError('options must be None')
|
||||
|
||||
def get_password(self, url, user, defer=True):
|
||||
if defer:
|
||||
return self
|
||||
return self()
|
||||
def _get_password(self, url, user):
|
||||
if self._password is None:
|
||||
self._password = getpass.getpass('Password: ')
|
||||
return self._password
|
||||
|
||||
def set_password(self, url, user, password):
|
||||
self._password = password
|
||||
@ -174,11 +200,6 @@ class TransientCredentialsManager(AbstractCredentialsManager):
|
||||
def delete_password(self, url, user):
|
||||
self._password = None
|
||||
|
||||
def __call__(self):
|
||||
if self._password is None:
|
||||
self._password = getpass.getpass('Password: ')
|
||||
return self._password
|
||||
|
||||
|
||||
class TransientDescriptor(AbstractCredentialsManagerDescriptor):
|
||||
def name(self):
|
||||
@ -214,7 +235,7 @@ class KeyringCredentialsManager(AbstractCredentialsManager):
|
||||
return None
|
||||
return super(cls, cls).create(cp, options)
|
||||
|
||||
def get_password(self, url, user, defer=True):
|
||||
def _get_password(self, url, user):
|
||||
self._load_backend()
|
||||
return keyring.get_password(urlsplit(url)[1], user)
|
||||
|
||||
@ -265,7 +286,7 @@ class GnomeKeyringCredentialsManager(AbstractCredentialsManager):
|
||||
return None
|
||||
return super(cls, cls).create(cp, options)
|
||||
|
||||
def get_password(self, url, user, defer=True):
|
||||
def _get_password(self, url, user):
|
||||
gk_data = self._keyring_data(url, user)
|
||||
if gk_data is None:
|
||||
return None
|
||||
|
Loading…
Reference in New Issue
Block a user