Add renewal session timeout

Fixes: https://pagure.io/koji/issue/3596
This commit is contained in:
Jana Cupova 2023-04-03 10:31:10 +02:00 committed by Tomas Kopecek
parent 8a1fc4954d
commit ce0ce20e3a
5 changed files with 83 additions and 3 deletions

View file

@ -112,6 +112,14 @@ General authentication options
Whether or not to automatically create a new user from valid ssl or gssapi credentials.
SessionRenewalTimeout
Type: integer
Default: ``1440``
The number of minutes before sessions are required to re-authenticate.
Set to 0 for no timeout.
GSSAPI authentication options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -553,4 +561,4 @@ We have default checksums types for create rpm checksums.
Default: ``md5 sha256``
Set RPM default checksums type. Default value is set upt to ``md5 sha256``.
Set RPM default checksums type. Default value is set up to ``md5 sha256``.

View file

@ -144,3 +144,6 @@ NotifyOnSuccess = True
## Determines default checksums
# RPMDefaultChecksums = md5 sha256
# The number of minutes before sessions are required to re-authenticate. Set to 0 for no timeout.
# SessionRenewalTimeout = 1440

View file

@ -26,6 +26,7 @@ import random
import re
import socket
import string
import time
import six
from six.moves import range, urllib
@ -137,7 +138,18 @@ class Session(object):
logger.warning("Session ID %s is not related to host IP %s.", self.id, hostip)
raise koji.AuthError('Invalid session or bad credentials')
# check for expiration
if not session_data['expired'] and context.opts['SessionRenewalTimeout'] != 0:
renewal_cutoff = (session_data['start_ts'] +
context.opts['SessionRenewalTimeout'] * 60)
if time.time() > renewal_cutoff:
session_data['expired'] = True
update = UpdateProcessor('sessions',
data={'expired': True},
clauses=['id = %(id)s OR master = %(id)s'],
values={'id': self.id})
update.execute()
context.cnx.commit()
if session_data['expired']:
if getattr(context, 'method') not in AUTH_METHODS:
raise koji.AuthExpired('session "%s" has expired' % self.id)

View file

@ -499,7 +499,9 @@ def load_config(environ):
['RegexNameInternal', 'string', r'^[A-Za-z0-9/_.+-]+$'],
['RegexUserName', 'string', r'^[A-Za-z0-9/_.@-]+$'],
['RPMDefaultChecksums', 'string', 'md5 sha256']
['RPMDefaultChecksums', 'string', 'md5 sha256'],
['SessionRenewalTimeout', 'integer', 1440],
]
opts = {}
for name, dtype, default in cfgmap:

View file

@ -49,6 +49,7 @@ class TestAuthSession(unittest.TestCase):
self.context.opts = {
'CheckClientIP': True,
'DisableURLSessions': False,
'SessionRenewalTimeout': 0,
}
with self.assertRaises(koji.GenericError) as cm:
kojihub.auth.Session()
@ -62,6 +63,7 @@ class TestAuthSession(unittest.TestCase):
self.context.opts = {
'CheckClientIP': True,
'DisableURLSessions': False,
'SessionRenewalTimeout': 0,
}
self.context.environ = {
'QUERY_STRING': 'session-id=123&session-key=xyz&callnum=345',
@ -88,6 +90,7 @@ class TestAuthSession(unittest.TestCase):
self.context.opts = {
'CheckClientIP': True,
'DisableURLSessions': True,
'SessionRenewalTimeout': 0,
}
self.context.environ = {
'HTTP_KOJI_SESSION_ID': '123',
@ -113,6 +116,58 @@ class TestAuthSession(unittest.TestCase):
def test_session_old(self):
self.get_session_old()
def test_renewal_timeout(self):
"""Simple kojihub.auth.Session instance"""
self.context.opts = {
'CheckClientIP': True,
'DisableURLSessions': False,
'SessionRenewalTimeout': 1440,
}
self.context.environ = {
'QUERY_STRING': 'session-id=123&session-key=xyz&callnum=345',
'REMOTE_ADDR': 'remote-addr',
}
self.query_executeOne.side_effect = [
{'authtype': 2, 'callnum': 1, "start_ts": 1666599426.227002,
"update_ts": 1666599426.254308, 'exclusive': None,
'expired': False, 'master': None,
'start_time': datetime.datetime(2022, 10, 24, 8, 17, 6, 227002,
tzinfo=datetime.timezone.utc),
'update_time': datetime.datetime(2022, 10, 24, 8, 17, 6, 254308,
tzinfo=datetime.timezone.utc),
'user_id': 1},
{'name': 'kojiadmin', 'status': 0, 'usertype': 0}]
with self.assertRaises(koji.GenericError) as cm:
kojihub.auth.Session()
# no args in request/environment
self.assertEqual(cm.exception.args[0], 'session "123" has expired')
self.assertEqual(len(self.updates), 1)
self.assertEqual(len(self.queries), 1)
update = self.updates[0]
self.assertEqual(update.table, 'sessions')
self.assertEqual(update.values['id'], 123)
self.assertEqual(update.clauses, ['id = %(id)s OR master = %(id)s'])
self.assertEqual(update.data, {'expired': True})
self.assertEqual(update.rawdata, {})
query = self.queries[0]
self.assertEqual(query.tables, ['sessions'])
self.assertEqual(query.joins, None)
self.assertEqual(query.clauses, ['closed IS FALSE', 'hostip = %(hostip)s', 'id = %(id)i',
'key = %(key)s'])
self.assertEqual(query.columns, ['authtype', 'callnum', 'exclusive', 'expired', 'master',
'start_time', "date_part('epoch', start_time)",
'update_time', "date_part('epoch', update_time)",
'user_id'])
self.assertEqual(query.aliases, ['authtype', 'callnum', 'exclusive', 'expired', 'master',
'start_time', 'start_ts', 'update_time', 'update_ts',
'user_id'])
self.assertEqual(query.values, {'id': 123, 'key': 'xyz', 'hostip': 'remote-addr'})
def test_basic_instance(self):
"""auth.Session instance"""
s, cntext = self.get_session()