From e6619a660f2e1f84bc337f6ea9fc55256e792618 Mon Sep 17 00:00:00 2001 From: "Piotr F. Mieszkowski" Date: Fri, 5 Apr 2024 17:19:01 +0200 Subject: [PATCH] When cleaning up after cron-job, remove expired queue items - Implement KeyConfirmationQueue.delete_expired_queue_items to delete items older than a given number of hours. - Introduce configuration parameter to specify maximum number of hours. It defaults to 1 hour. - Update documentation to explain that we never assign ST_TO_BE_DELETED. --- lacre.conf.sample | 4 ++++ lacre/dbschema.py | 3 ++- lacre/repositories.py | 12 ++++++++++++ webgate-cron.py | 13 ++++++++----- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lacre.conf.sample b/lacre.conf.sample index a8c0aff..fd20f10 100644 --- a/lacre.conf.sample +++ b/lacre.conf.sample @@ -133,6 +133,10 @@ pooling_mode = optimistic # made and closed after use, to avoid pool growth and connection rejections. #max_overflow = 10 +# Number of hours we will wait for the user to confirm their email. Cron-job +# will delete items older than this number of hours. Default: 1h. +#max_queue_hours = 1 + [enc_keymap] # You can find these by running the following command: # gpg --list-keys --keyid-format long user@example.com diff --git a/lacre/dbschema.py b/lacre/dbschema.py index 740ff68..4b97144 100644 --- a/lacre/dbschema.py +++ b/lacre/dbschema.py @@ -16,7 +16,8 @@ import sqlalchemy # Values for lacre_keys.status column: # - ST_DEFAULT: initial state; # - ST_IMPORTED: key has been successfully processed by cron job; -# - ST_TO_BE_DELETED: key can be deleted. +# - ST_TO_BE_DELETED: key can be deleted. We only have checks for this value +# but never assign it, so this is a candidate for removal. ST_DEFAULT, ST_IMPORTED, ST_TO_BE_DELETED = range(3) # lacre_keys.confirmed is set to an empty string when a key is confirmed by the user. diff --git a/lacre/repositories.py b/lacre/repositories.py index 68cf54c..0c785af 100644 --- a/lacre/repositories.py +++ b/lacre/repositories.py @@ -1,5 +1,7 @@ """Lacre identity and key repositories.""" +from datetime import timedelta + from sqlalchemy import create_engine, select, delete, and_, func from sqlalchemy.exc import OperationalError import logging @@ -171,6 +173,16 @@ class KeyConfirmationQueue: with self._engine.connect() as conn: return [e for e in conn.execute(seldel)] + def delete_expired_queue_items(self, when): + max_hours = get_item('database', 'max_queue_hours', 1) + oldest = when - timedelta(hours=max_hours) + + delq = delete(self._keys).where(self._keys.c.time < oldest) + LOG.debug('Deleting queue items older than %s: %s', repr(oldest), delq) + + with self._engine.connect() as conn: + conn.execute(delq) + def delete_keys(self, row_id, /, email=None): """Remove key from the database.""" if email is not None: diff --git a/webgate-cron.py b/webgate-cron.py index c4f0d73..110ab3c 100755 --- a/webgate-cron.py +++ b/webgate-cron.py @@ -20,6 +20,7 @@ # import sys +from datetime import datetime import logging import lacre import lacre.config as conf @@ -56,7 +57,7 @@ def import_key(key_dir, armored_key, key_id, email, key_queue, identities): def import_failed(key_id, email, key_queue): key_queue.delete_keys(key_id) - LOG.warning('Import confirmation failed: %s', email) + LOG.warning('Key confirmation failed: %s', email) if conf.flag_enabled('cron', 'send_email'): notify("PGP key registration failed", "registrationError.md", email) @@ -64,17 +65,16 @@ def import_failed(key_id, email, key_queue): def delete_key(key_id, email, key_queue): # delete key so we don't continue processing it - LOG.debug('Empty key received, just deleting') + LOG.debug('Empty key received, deleting known key from: %s', email) - key_queue.delete_keys(row_id) + key_queue.delete_keys(key_id, email) if conf.flag_enabled('cron', 'send_email'): notify("PGP key deleted", "keyDeleted.md", email) def cleanup(key_dir, key_queue): """Delete keys and queue entries.""" - - LOG.info('Cleaning up after a round of key confirmation') + LOG.debug('Removing no longer needed keys from queue') for email, row_id in key_queue.fetch_keys_to_delete(): LOG.debug('Removing key from keyring: %s', email) GnuPG.delete_key(key_dir, email) @@ -84,6 +84,9 @@ def cleanup(key_dir, key_queue): LOG.info('Deleted key for: %s', email) + now = datetime.now() + key_queue.delete_expired_queue_items(now) + _validate_config()