don't use filelocks for writing password because there only is a single doveauth process anyway

This commit is contained in:
holger krekel
2024-07-25 14:19:51 +02:00
parent 4f4fd6a90c
commit eaff92cebc
2 changed files with 16 additions and 13 deletions

View File

@@ -3,6 +3,7 @@ import json
import logging
import os
import sys
from threading import RLock
from .config import Config, read_config
from .dictproxy import DictProxy
@@ -85,6 +86,10 @@ class AuthDictProxy(DictProxy):
def __init__(self, config):
super().__init__()
self.config = config
# We serialize all password-writes in the single doveauth process
# so that threads can not mangle the password when writing.
# Setting a password is a quite rare event anyway.
self._password_write_lock = RLock()
def handle_lookup(self, parts):
# Dovecot <2.3.17 has only one part,
@@ -140,7 +145,8 @@ class AuthDictProxy(DictProxy):
if not is_allowed_to_create(self.config, addr, cleartext_password):
return
user.set_password(encrypt_password(cleartext_password))
with self._password_write_lock:
user.set_password(encrypt_password(cleartext_password))
print(f"Created address: {user}", file=sys.stderr)
return user.get_userdb_dict()

View File

@@ -1,8 +1,6 @@
import logging
import os
import filelock
def get_daytimestamp(timestamp) -> int:
return int(timestamp) // 86400 * 86400
@@ -39,20 +37,19 @@ class User:
def set_password(self, enc_password):
"""Set the specified password for this user.
If called concurrently from multiple threads
the last password set call will be persisted.
NOTE that this method is not multi-thread/process safe.
The caller has to ensure only a single thread writes to the same
user's password file.
"""
self.maildir.mkdir(exist_ok=True)
lock_path = self.maildir.joinpath("password.lock")
password = enc_password.encode("ascii")
with filelock.FileLock(lock_path):
try:
self.password_path.write_bytes(password)
except PermissionError:
if not self.addr.startswith("echo@"):
logging.error(f"could not write password for: {self.addr}")
raise
try:
self.password_path.write_bytes(password)
except PermissionError:
if not self.addr.startswith("echo@"):
logging.error(f"could not write password for: {self.addr}")
raise
def set_last_login_timestamp(self, timestamp):
"""Track login time with daily granularity