From 29e116448df7b8b862e6a9bb3dd719d58b1c6915 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Thu, 10 Nov 2022 15:18:20 +0100 Subject: [PATCH] store original auth method --- koji/__init__.py | 96 +++++++++++++++++++++++++++++++++--------------- koji/auth.py | 12 +++--- 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/koji/__init__.py b/koji/__init__.py index 883550ae..d1166363 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -2408,9 +2408,6 @@ def grab_session_options(options): 'upload_blocksize', 'no_ssl_verify', 'serverca', - 'keytab', - 'principal', - 'ccache', ) # cert is omitted for now if isinstance(options, dict): @@ -2427,7 +2424,13 @@ def grab_session_options(options): class ClientSession(object): - def __init__(self, baseurl, opts=None, sinfo=None): + def __init__(self, baseurl, opts=None, sinfo=None, auth_method=None): + """ + :param baseurl str: hub url + :param dict opts: dictionary with content varying according to authentication method + :param dict sinfo: session info returned by login method + :param dict auth_method: method for reauthentication, shouldn't be ever set manually + """ assert baseurl, "baseurl argument must not be empty" if opts is None: opts = {} @@ -2448,7 +2451,7 @@ class ClientSession(object): self.new_session() self.opts.setdefault('timeout', DEFAULT_REQUEST_TIMEOUT) self.exclusive = False - self.hostip = None + self.auth_method = auth_method @property def multicall(self): @@ -2487,9 +2490,18 @@ class ClientSession(object): self.session_key = sinfo['session-key'] self.sinfo = sinfo - def login(self, opts=None): + def login(self, opts=None, session_key=None): + """ + Username/password based login method + + :param dict opts: dict used by hub "login" call, currently can + contain only "host_ip" key. + :returns bool True: success or raises exception + """ + # store calling parameters + self.auth_method = {'method': 'login', 'kwargs': {'opts': opts}} sinfo = self.callMethod('login', self.opts['user'], self.opts['password'], - self.opts['session_key'], opts) + opts=opts, session_key=session_key) if not sinfo: return False self.setSession(sinfo) @@ -2499,14 +2511,33 @@ class ClientSession(object): def subsession(self): "Create a subsession" sinfo = self.callMethod('subsession') - return type(self)(self.baseurl, self.opts, sinfo) + return type(self)(self.baseurl, opts=self.opts, sinfo=sinfo, auth_method=self.auth_method) def gssapi_login(self, principal=None, keytab=None, ccache=None, proxyuser=None, proxyauthtype=None, session_key=None): + """ + GSSAPI/Kerberos login method + + :param str principal: Kerberos principal + :param str keytab: path to keytab file + :param str ccache: path to ccache file/dir + :param str proxyuser: name of proxied user (e.g. forwarding by web ui) + :param int proxyauthtype: AUTHTYPE used by proxied user (can be different from ours) + :param str session_key: used for session renewal + :returns bool True: success or raises exception + """ if not reqgssapi: raise PythonImportError( "Please install python-requests-gssapi to use GSSAPI." ) + # store calling parameters + self.auth_method = { + 'method': 'gssapi_login', + 'kwargs': { + 'principal': principal, 'keytab': keytab, 'ccache': ccache, 'proxyuser': proxyuser, + 'proxyauthtype': proxyauthtype, 'session_key': session_key + } + } # force https old_baseurl = self.baseurl uri = six.moves.urllib.parse.urlsplit(self.baseurl) @@ -2597,6 +2628,26 @@ class ClientSession(object): def ssl_login(self, cert=None, ca=None, serverca=None, proxyuser=None, proxyauthtype=None, session_key=None): + """ + SSL cert based login + + :param str cert: path to SSL certificate + :param str ca: deprecated, not used anymore + :param str serverca: path for CA public cert, otherwise system-wide CAs are used + :param str proxyuser: name of proxied user (e.g. forwarding by web ui) + :param int proxyauthtype: AUTHTYPE used by proxied user (can be different from ours) + :param str session_key: used for session renewal + :returns bool: success + """ + # store calling parameters + self.logger.error("ssl_login---------------") + self.auth_method = { + 'method': 'ssl_login', + 'kwargs': { + 'cert': cert, 'ca': ca, 'serverca': serverca, 'proxyuser': proxyuser, + 'proxyauthtype': proxyauthtype, 'session_key': session_key, + } + } cert = cert or self.opts.get('cert') serverca = serverca or self.opts.get('serverca') if cert is None: @@ -2843,28 +2894,15 @@ class ClientSession(object): return result def _renew_session(self): - session_key = self.session_key + if not hasattr(self, 'auth_method'): + raise GenericError("Missing info for reauthentication") + # will be deleted by setSession + auth_method = self.auth_method['method'] + args = self.auth_method.get('args', []) + kwargs = self.auth_method.get('kwargs', {}) + kwargs['session_key'] = self.session_key self.setSession(None) - if self.authtype == 'SSL' or \ - (self.opts.get('cert') and os.path.isfile(self.opts['cert'])): - self.ssl_login(cert=self.opts['cert'], - serverca=self.opts['serverca'], - session_key=session_key) - elif self.authtype == 'NORMAL' or self.opts.get('user'): - self.login(user=self.opts['user'], password=self.opts['password'], - session_key=session_key) - elif self.authtype in ['KERBEROS', 'GSSAPI'] or \ - self.opts.get('krb_principal'): - authtype = self.authtype or AUTHTYPES['GSSAPI'] - principal = self.opts.get('principal') - keytab = self.opts.get('keytab') - ccache = self.opts.get('ccache') - if authtype == 'KERBEROS': - self.krb_login(principal=principal, keytab=keytab, - ccache=ccache, session_key=session_key) - elif authtype == 'GSSAPI': - self.gssapi_login(self, principal=principal, keytab=keytab, - ccache=ccache, session_key=session_key) + auth_method(*args, **kwargs) if self.exclusive: self.exclusiveSession() diff --git a/koji/auth.py b/koji/auth.py index 2da0e377..a5ac19fd 100644 --- a/koji/auth.py +++ b/koji/auth.py @@ -280,7 +280,7 @@ class Session(object): if result['status'] != koji.USER_STATUS['NORMAL']: raise koji.AuthError('logins by %s are not allowed' % result['name']) - def login(self, user, password, session_key=None, opts=None): + def login(self, user, password, opts=None, session_key=None): """create a login session""" if opts is None: opts = {} @@ -491,14 +491,14 @@ class Session(object): If master is specified, create a subsession """ if session_key: + if master: + raise koji.GenericError("Can't call createSession with both master + session_key.") query = QueryProcessor(tables=['sessions'], columns=['master'], clauses=['key=%(session_key)d'], values={'session_key': session_key}) - row = query.executeOne(strict=False) - if not row: - raise koji.GenericError("Don't allow to renew subsession, " - "subsession doesn't exist.") - master = row['master'] + master = query.singleValue(strict=False) + if not master: + raise koji.GenericError("Don't allow to renew non-existent subsession") # generate a random key alnum = string.ascii_letters + string.digits