mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
Compare commits
1 Commits
link2xt/av
...
hpk/stream
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca763960e5 |
@@ -21,19 +21,32 @@ def encrypt_password(password: str):
|
||||
return "{SHA512-CRYPT}" + passhash
|
||||
|
||||
|
||||
def check_password(password) -> bool:
|
||||
"""Check password policy"""
|
||||
if len(password) < 10:
|
||||
def is_allowed_to_create(user, cleartext_password) -> bool:
|
||||
"""Return True if user and password are admissable."""
|
||||
if os.path.exists(NOCREATE_FILE):
|
||||
logging.warning(f"blocked account creation because {NOCREATE_FILE!r} exists.")
|
||||
return False
|
||||
|
||||
if len(cleartext_password) < 10:
|
||||
logging.warning("Password needs to be at least 10 characters long")
|
||||
return False
|
||||
|
||||
parts = user.split("@")
|
||||
if len(parts) != 2:
|
||||
logging.warning(f"user {user!r} is not a proper e-mail address")
|
||||
return False
|
||||
localpart, domain = parts
|
||||
|
||||
if domain == "nine.testrun.org":
|
||||
# nine.testrun.org policy, username has to be exactly nine chars
|
||||
if len(localpart) != 9:
|
||||
logging.warning(f"localpart {localpart!r} has not exactly nine chars")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def create_user(db, user, encrypted_password):
|
||||
if os.path.exists(NOCREATE_FILE):
|
||||
logging.warning(
|
||||
f"Didn't create account: {NOCREATE_FILE} exists. Delete the file to enable account creation."
|
||||
)
|
||||
return
|
||||
with db.write_transaction() as conn:
|
||||
conn.create_user(user, encrypted_password)
|
||||
return dict(
|
||||
@@ -57,13 +70,13 @@ def lookup_userdb(db, user):
|
||||
return get_user_data(db, user)
|
||||
|
||||
|
||||
def lookup_passdb(db, user, password):
|
||||
def lookup_passdb(db, user, cleartext_password):
|
||||
userdata = get_user_data(db, user)
|
||||
if not userdata:
|
||||
if not check_password(password):
|
||||
logging.warning("Attempt to create an account with a weak password.")
|
||||
if not is_allowed_to_create(user, cleartext_password):
|
||||
return
|
||||
return create_user(db, user, encrypt_password(password))
|
||||
encrypted_password = encrypt_password(cleartext_password)
|
||||
userdata = create_user(db=db, user=user, encrypted_password=encrypted_password)
|
||||
userdata["password"] = userdata["password"].strip()
|
||||
return userdata
|
||||
|
||||
@@ -87,7 +100,7 @@ def handle_dovecot_request(msg, db, mail_domain):
|
||||
reply_command = "N"
|
||||
elif type == "passdb":
|
||||
if user.endswith(f"@{mail_domain}"):
|
||||
res = lookup_passdb(db, user, password=args[0])
|
||||
res = lookup_passdb(db, user, cleartext_password=args[0])
|
||||
if res:
|
||||
reply_command = "O"
|
||||
else:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
import chatmaild.dictproxy
|
||||
from chatmaild.dictproxy import get_user_data, lookup_passdb
|
||||
from chatmaild.dictproxy import get_user_data, lookup_passdb, handle_dovecot_request
|
||||
from chatmaild.database import Database, DBError
|
||||
|
||||
|
||||
@@ -14,13 +15,13 @@ def db(tmpdir):
|
||||
return Database(db_path)
|
||||
|
||||
|
||||
|
||||
def test_basic(db):
|
||||
chatmaild.dictproxy.NOCREATE_FILE = "/tmp/nocreate"
|
||||
if os.path.exists(chatmaild.dictproxy.NOCREATE_FILE):
|
||||
os.remove(chatmaild.dictproxy.NOCREATE_FILE)
|
||||
lookup_passdb(db, "link2xt@c1.testrun.org", "Pieg9aeToe3eghuthe5u")
|
||||
data = get_user_data(db, "link2xt@c1.testrun.org")
|
||||
assert data
|
||||
data2 = lookup_passdb(db, "link2xt@c1.testrun.org", "Pieg9aeToe3eghuthe5u")
|
||||
assert data == data2
|
||||
|
||||
|
||||
def test_dont_overwrite_password_on_wrong_login(db):
|
||||
@@ -32,14 +33,12 @@ def test_dont_overwrite_password_on_wrong_login(db):
|
||||
assert res["password"] == res2["password"]
|
||||
|
||||
|
||||
def test_nocreate_file(db):
|
||||
chatmaild.dictproxy.NOCREATE_FILE = "/tmp/nocreate"
|
||||
with open(chatmaild.dictproxy.NOCREATE_FILE, "w+") as f:
|
||||
f.write("")
|
||||
assert os.path.exists(chatmaild.dictproxy.NOCREATE_FILE)
|
||||
def test_nocreate_file(db, monkeypatch, tmpdir):
|
||||
p = tmpdir.join("nocreate")
|
||||
p.write("")
|
||||
monkeypatch.setattr(chatmaild.dictproxy, "NOCREATE_FILE", str(p))
|
||||
lookup_passdb(db, "newuser1@something.org", "zequ0Aimuchoodaechik")
|
||||
assert not get_user_data(db, "newuser1@something.org")
|
||||
os.remove(chatmaild.dictproxy.NOCREATE_FILE)
|
||||
|
||||
|
||||
def test_db_version(db):
|
||||
@@ -51,3 +50,15 @@ def test_too_high_db_version(db):
|
||||
conn.execute("PRAGMA user_version=%s;" % (999,))
|
||||
with pytest.raises(DBError):
|
||||
db.ensure_tables()
|
||||
|
||||
|
||||
def test_handle_dovecot_request(db):
|
||||
msg = ('Lshared/passdb/laksjdlaksjdlaksjdlk12j3l1k2j3123/'
|
||||
'some42@c3.testrun.org\tsome42@c3.testrun.org')
|
||||
res = handle_dovecot_request(msg, db, "c3.testrun.org")
|
||||
assert res
|
||||
assert res[0] == "O" and res.endswith("\n")
|
||||
userdata = json.loads(res[1:].strip())
|
||||
assert userdata["home"] == "/home/vmail/some42@c3.testrun.org"
|
||||
assert userdata["uid"] == userdata["gid"] == "vmail"
|
||||
assert userdata["password"].startswith("{SHA512-CRYPT}")
|
||||
|
||||
@@ -195,8 +195,8 @@ def gencreds(maildomain):
|
||||
num = next(count)
|
||||
alphanumeric = "abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
user = "".join(random.choices(alphanumeric, k=10))
|
||||
user = f"ac{num}_{user}"
|
||||
password = "".join(random.choices(alphanumeric, k=10))
|
||||
user = f"ac{num}_{user}"[:9]
|
||||
password = "".join(random.choices(alphanumeric, k=12))
|
||||
yield f"{user}@{domain}", f"{password}"
|
||||
|
||||
return lambda domain=None: next(gen(domain))
|
||||
|
||||
Reference in New Issue
Block a user