1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-01-13 09:16:14 +01:00

Merge pull request #1136 from dmach/password-prompt-user-apiurl

Print user and apiurl when prompting for a password
This commit is contained in:
Daniel Mach 2022-09-08 11:13:42 +02:00 committed by GitHub
commit 215d09cdc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 21 deletions

View File

@ -794,7 +794,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, defer=True)
password = creds_mgr.get_password(url, user, defer=True, apiurl=apiurl)
if password is None:
raise oscerr.ConfigMissingCredentialsError('No password found in section %s' % url, conffile, url)

View File

@ -53,6 +53,7 @@ class MockRequest:
def enable_http_debug(config):
if not int(config["http_debug"]) and not int(config["http_full_debug"]):
http.client.print = lambda *args, **kwargs: None
return
# HACK: override HTTPResponse's init to increase debug level
@ -259,9 +260,9 @@ def http_request(method, url, headers=None, data=None, file=None):
CONNECTION_POOLS[apiurl] = pool
auth_handlers = [
CookieJarAuthHandler(os.path.expanduser(conf.config["cookiejar"])),
SignatureAuthHandler(options["user"], options["sshkey"], options["pass"]),
BasicAuthHandler(options["user"], options["pass"]),
CookieJarAuthHandler(apiurl, os.path.expanduser(conf.config["cookiejar"])),
SignatureAuthHandler(apiurl, options["user"], options["sshkey"], options["pass"]),
BasicAuthHandler(apiurl, options["user"], options["pass"]),
]
for handler in auth_handlers:
@ -370,6 +371,9 @@ def http_DELETE(*args, **kwargs):
class AuthHandlerBase:
def __init__(self, apiurl):
self.apiurl = apiurl
def _get_auth_schemes(self, response):
"""
Extract all `www-authenticate` headers from `response` and return them
@ -425,7 +429,8 @@ class CookieJarAuthHandler(AuthHandlerBase):
# Shared among instances, instantiate on first use, key equals to cookiejar path.
COOKIEJARS = {}
def __init__(self, cookiejar_path):
def __init__(self, apiurl, cookiejar_path):
super().__init__(apiurl)
self.cookiejar_path = cookiejar_path
if self.cookiejar_path in self.COOKIEJARS:
self.cookiejar_lock_path = None
@ -486,7 +491,8 @@ class CookieJarAuthHandler(AuthHandlerBase):
class BasicAuthHandler(AuthHandlerBase):
def __init__(self, user, password):
def __init__(self, apiurl, user, password):
super().__init__(apiurl)
self.user = user
self.password = password
@ -507,15 +513,16 @@ class BasicAuthHandler(AuthHandlerBase):
class SignatureAuthHandler(AuthHandlerBase):
def __init__(self, user, sshkey, basic_auth_password=None):
def __init__(self, apiurl, user, sshkey, basic_auth_password=None):
super().__init__(apiurl)
self.user = user
self.sshkey = sshkey
self.ssh_keygen_path = shutil.which("ssh-keygen")
self.ssh_add_path = shutil.which("ssh-add")
apiurl = conf.config["apiurl"]
if conf.config["api_host_options"][apiurl].get("credentials_mgr_class", None) == "osc.credentials.TransientCredentialsManager":
creds_mgr = conf.config["api_host_options"][self.apiurl].get("credentials_mgr_class", None)
if creds_mgr == "osc.credentials.TransientCredentialsManager":
self.basic_auth_password = False
else:
# value of `basic_auth_password` is only used as a hint if we should skip signature auth

View File

@ -80,14 +80,14 @@ class AbstractCredentialsManager:
def create(cls, cp, options):
return cls(cp, options)
def _get_password(self, url, user):
def _get_password(self, url, user, apiurl=None):
raise NotImplementedError()
def get_password(self, url, user, defer=True):
def get_password(self, url, user, defer=True, apiurl=None):
if defer:
return _LazyPassword(lambda: self._get_password(url, user))
return _LazyPassword(lambda: self._get_password(url, user, apiurl=apiurl))
else:
return self._get_password(url, user)
return self._get_password(url, user, apiurl=apiurl)
def set_password(self, url, user, password):
raise NotImplementedError()
@ -103,7 +103,7 @@ class AbstractCredentialsManager:
class PlaintextConfigFileCredentialsManager(AbstractCredentialsManager):
def get_password(self, url, user, defer=True):
def get_password(self, url, user, defer=True, apiurl=None):
return self._cp.get(url, 'pass', raw=True)
def set_password(self, url, user, password):
@ -132,13 +132,12 @@ class PlaintextConfigFileDescriptor(AbstractCredentialsManagerDescriptor):
return PlaintextConfigFileCredentialsManager(cp, None)
class ObfuscatedConfigFileCredentialsManager(
PlaintextConfigFileCredentialsManager):
def get_password(self, url, user, defer=True):
class ObfuscatedConfigFileCredentialsManager(PlaintextConfigFileCredentialsManager):
def get_password(self, url, user, defer=True, apiurl=None):
if self._cp.has_option(url, 'passx', proper=True):
passwd = self._cp.get(url, 'passx', raw=True)
else:
passwd = super(self.__class__, self).get_password(url, user)
passwd = super(self.__class__, self).get_password(url, user, apiurl=apiurl)
return self.decode_password(passwd)
def set_password(self, url, user, password):
@ -182,9 +181,15 @@ class TransientCredentialsManager(AbstractCredentialsManager):
if options is not None:
raise RuntimeError('options must be None')
def _get_password(self, url, user):
def _get_password(self, url, user, apiurl=None):
if self._password is None:
self._password = getpass.getpass('Password: ')
if apiurl:
# strip scheme from apiurl because we don't want to display it to the user
apiurl_no_scheme = urlsplit(apiurl)[1]
msg = f'Password [{user}@{apiurl_no_scheme}]: '
else:
msg = 'Password: '
self._password = getpass.getpass(msg)
return self._password
def set_password(self, url, user, password):
@ -229,7 +234,7 @@ class KeyringCredentialsManager(AbstractCredentialsManager):
return None
return super(cls, cls).create(cp, options)
def _get_password(self, url, user):
def _get_password(self, url, user, apiurl=None):
self._load_backend()
return keyring.get_password(urlsplit(url)[1], user)