##// END OF EJS Templates
pull-requests: added commit flow into pr listing tables
pull-requests: added commit flow into pr listing tables

File last commit:

r5157:2ffcea0c default
r5170:a77dd674 default
Show More
user_sessions.py
275 lines | 8.5 KiB | text/x-python | PythonLexer
copyrights: updated for 2023
r5088 # Copyright (C) 2017-2023 RhodeCode GmbH
sessions: added interface to show, and cleanup user auth sessions.
r1295 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
user-sessions: add count and cleanup of file-based sessions.
r1364 import os
user sessions: get ability to count memcached sessions
r2141 import re
user-sessions: add count and cleanup of file-based sessions.
r1364 import time
sessions: added interface to show, and cleanup user auth sessions.
r1295 import datetime
sessions: fixed more problems with redis sessions cleanup errors
r5157
import binascii
sessions: added interface to show, and cleanup user auth sessions.
r1295 import dateutil
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 import dateutil.relativedelta
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 import pickle
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 import base64
user sessions: get ability to count memcached sessions
r2141
sessions: added interface to show, and cleanup user auth sessions.
r1295 from rhodecode.model.db import DbSession, Session
class CleanupCommand(Exception):
pass
class BaseAuthSessions(object):
SESSION_TYPE = None
user-sessions: add count and cleanup of file-based sessions.
r1364 NOT_AVAILABLE = 'NOT AVAILABLE'
sessions: added interface to show, and cleanup user auth sessions.
r1295
def __init__(self, config):
session_conf = {}
core: multiple fixes to unicode vs str usage...
r5065 for k, v in list(config.items()):
sessions: added interface to show, and cleanup user auth sessions.
r1295 if k.startswith('beaker.session'):
session_conf[k] = v
self.config = session_conf
def get_count(self):
raise NotImplementedError
user-sessions: fix other backend expired count, and fixed some helper text.
r1298 def get_expired_count(self, older_than_seconds=None):
sessions: added interface to show, and cleanup user auth sessions.
r1295 raise NotImplementedError
def clean_sessions(self, older_than_seconds=None):
raise NotImplementedError
def _seconds_to_date(self, seconds):
return datetime.datetime.utcnow() - dateutil.relativedelta.relativedelta(
seconds=seconds)
class DbAuthSessions(BaseAuthSessions):
SESSION_TYPE = 'ext:database'
def get_count(self):
return DbSession.query().count()
def get_expired_count(self, older_than_seconds=None):
expiry_date = self._seconds_to_date(older_than_seconds)
return DbSession.query().filter(DbSession.accessed < expiry_date).count()
def clean_sessions(self, older_than_seconds=None):
expiry_date = self._seconds_to_date(older_than_seconds)
user-sessions: added an API call to cleanup sessions.
r1367 to_remove = DbSession.query().filter(DbSession.accessed < expiry_date).count()
sessions: added interface to show, and cleanup user auth sessions.
r1295 DbSession.query().filter(DbSession.accessed < expiry_date).delete()
Session().commit()
user-sessions: added an API call to cleanup sessions.
r1367 return to_remove
sessions: added interface to show, and cleanup user auth sessions.
r1295
class FileAuthSessions(BaseAuthSessions):
SESSION_TYPE = 'file sessions'
user-sessions: add count and cleanup of file-based sessions.
r1364 def _get_sessions_dir(self):
data_dir = self.config.get('beaker.session.data_dir')
return data_dir
sessions: added interface to show, and cleanup user auth sessions.
r1295
user-sessions: add count and cleanup of file-based sessions.
r1364 def _count_on_filesystem(self, path, older_than=0, callback=None):
user-sessions: added an API call to cleanup sessions.
r1367 value = dict(percent=0, used=0, total=0, items=0, callbacks=0,
path=path, text='')
user-sessions: add count and cleanup of file-based sessions.
r1364 items_count = 0
used = 0
user-sessions: added an API call to cleanup sessions.
r1367 callbacks = 0
user-sessions: add count and cleanup of file-based sessions.
r1364 cur_time = time.time()
for root, dirs, files in os.walk(path):
for f in files:
final_path = os.path.join(root, f)
try:
mtime = os.stat(final_path).st_mtime
if (cur_time - mtime) > older_than:
items_count += 1
if callback:
callback_res = callback(final_path)
user-sessions: added an API call to cleanup sessions.
r1367 callbacks += 1
user-sessions: add count and cleanup of file-based sessions.
r1364 else:
used += os.path.getsize(final_path)
except OSError:
pass
value.update({
'percent': 100,
'used': used,
'total': used,
user-sessions: added an API call to cleanup sessions.
r1367 'items': items_count,
'callbacks': callbacks
user-sessions: add count and cleanup of file-based sessions.
r1364 })
return value
def get_count(self):
try:
sessions_dir = self._get_sessions_dir()
items_count = self._count_on_filesystem(sessions_dir)['items']
except Exception:
items_count = self.NOT_AVAILABLE
return items_count
def get_expired_count(self, older_than_seconds=0):
try:
sessions_dir = self._get_sessions_dir()
items_count = self._count_on_filesystem(
sessions_dir, older_than=older_than_seconds)['items']
except Exception:
items_count = self.NOT_AVAILABLE
return items_count
def clean_sessions(self, older_than_seconds=0):
# find . -mtime +60 -exec rm {} \;
sessions_dir = self._get_sessions_dir()
def remove_item(path):
os.remove(path)
user-sessions: added an API call to cleanup sessions.
r1367 stats = self._count_on_filesystem(
user-sessions: add count and cleanup of file-based sessions.
r1364 sessions_dir, older_than=older_than_seconds,
user-sessions: added an API call to cleanup sessions.
r1367 callback=remove_item)
return stats['callbacks']
sessions: added interface to show, and cleanup user auth sessions.
r1295
class MemcachedAuthSessions(BaseAuthSessions):
SESSION_TYPE = 'ext:memcached'
user sessions: get ability to count memcached sessions
r2141 _key_regex = re.compile(r'ITEM (.*_session) \[(.*); (.*)\]')
def _get_client(self):
import memcache
client = memcache.Client([self.config.get('beaker.session.url')])
return client
def _get_telnet_client(self, host, port):
import telnetlib
client = telnetlib.Telnet(host, port, None)
return client
def _run_telnet_cmd(self, client, cmd):
client.write("%s\n" % cmd)
return client.read_until('END')
def key_details(self, client, slab_ids, limit=100):
""" Return a list of tuples containing keys and details """
cmd = 'stats cachedump %s %s'
for slab_id in slab_ids:
modernize: python3 updates
r5096 yield from self._key_regex.finditer(
self._run_telnet_cmd(client, cmd % (slab_id, limit)))
sessions: added interface to show, and cleanup user auth sessions.
r1295
def get_count(self):
user sessions: get ability to count memcached sessions
r2141 client = self._get_client()
count = self.NOT_AVAILABLE
try:
slabs = []
for server, slabs_data in client.get_slabs():
python3: fixed various code issues...
r4973 slabs.extend(list(slabs_data.keys()))
user sessions: get ability to count memcached sessions
r2141
host, port = client.servers[0].address
telnet_client = self._get_telnet_client(host, port)
keys = self.key_details(telnet_client, slabs)
count = 0
for _k in keys:
count += 1
except Exception:
return count
return count
sessions: added interface to show, and cleanup user auth sessions.
r1295
user-sessions: fix other backend expired count, and fixed some helper text.
r1298 def get_expired_count(self, older_than_seconds=None):
user-sessions: add count and cleanup of file-based sessions.
r1364 return self.NOT_AVAILABLE
sessions: added interface to show, and cleanup user auth sessions.
r1295
def clean_sessions(self, older_than_seconds=None):
raise CleanupCommand('Cleanup for this session type not yet available')
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 class RedisAuthSessions(BaseAuthSessions):
SESSION_TYPE = 'ext:redis'
def _get_client(self):
import redis
args = {
'socket_timeout': 60,
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 'decode_responses': False,
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 'url': self.config.get('beaker.session.url')
}
client = redis.StrictRedis.from_url(**args)
return client
def get_count(self):
client = self._get_client()
return len(client.keys('beaker_cache:*'))
def get_expired_count(self, older_than_seconds=None):
expiry_date = self._seconds_to_date(older_than_seconds)
return self.NOT_AVAILABLE
def clean_sessions(self, older_than_seconds=None):
client = self._get_client()
expiry_time = time.time() - older_than_seconds
deleted_keys = 0
redis-sessions: fixed cleanup command failing on serialized errors....
r5134
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 for key in client.keys('beaker_cache:*'):
data = client.get(key)
if data:
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 accessed_time = 0
user-sessions: fix cleanup with corrupted session data.
r4483 try:
sessions: fixed more problems with redis sessions cleanup errors
r5157 data = base64.b64decode(data)
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 json_data = pickle.loads(data)
user-sessions: fix cleanup with corrupted session data.
r4483 accessed_time = json_data['_accessed_time']
sessions: fixed more problems with redis sessions cleanup errors
r5157 except binascii.Error:
accessed_time = 0
redis-sessions: fixed cleanup command failing on serialized errors....
r5134 except pickle.UnpicklingError:
accessed_time = 0
user-sessions: fix cleanup with corrupted session data.
r4483 except KeyError:
accessed_time = 0
redis-sessions: fixed cleanup command failing on serialized errors....
r5134
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 if accessed_time < expiry_time:
client.delete(key)
deleted_keys += 1
return deleted_keys
sessions: added interface to show, and cleanup user auth sessions.
r1295 class MemoryAuthSessions(BaseAuthSessions):
SESSION_TYPE = 'memory'
def get_count(self):
user-sessions: add count and cleanup of file-based sessions.
r1364 return self.NOT_AVAILABLE
sessions: added interface to show, and cleanup user auth sessions.
r1295
user-sessions: fix other backend expired count, and fixed some helper text.
r1298 def get_expired_count(self, older_than_seconds=None):
user-sessions: add count and cleanup of file-based sessions.
r1364 return self.NOT_AVAILABLE
sessions: added interface to show, and cleanup user auth sessions.
r1295
def clean_sessions(self, older_than_seconds=None):
raise CleanupCommand('Cleanup for this session type not yet available')
def get_session_handler(session_type):
types = {
'file': FileAuthSessions,
'ext:memcached': MemcachedAuthSessions,
user-sessions: added option to cleanup redis based sessions in user session interface.
r3539 'ext:redis': RedisAuthSessions,
sessions: added interface to show, and cleanup user auth sessions.
r1295 'ext:database': DbAuthSessions,
'memory': MemoryAuthSessions
}
try:
return types[session_type]
except KeyError:
raise ValueError(
modernize: python3 updates
r5096 f'This type {session_type} is not supported')