1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-01-24 22:06:14 +01:00

Only prompt for a password if the server asks for it

In many cases the session cookie is already available, so there
is no need to ask for a password. To make this work with the
python authentication implementation, we add a small proxy object
for the password and only ask the credential manager if the
stringify method is called.

This approach also makes it possible to offer a non-password based
authorization type if the server allows multiple authentication
methods.
This commit is contained in:
Michael Schroeder 2022-04-04 10:15:35 +02:00
parent 34575f8b74
commit 784d330f20
No known key found for this signature in database
GPG Key ID: 6AEFFADDED5BE4F9
2 changed files with 42 additions and 19 deletions

View File

@ -764,7 +764,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:
@ -884,6 +884,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
@ -979,7 +981,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)

View File

@ -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