cli: apply min-length password policy in create-user

Same validate_new_password used by the web change-password flow,
so the policy is enforced uniformly across CLI and HTTP entry
points. Existing CLI tests bumped to passwords that satisfy the
new floor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-11 22:00:16 +02:00
parent 224b023ca0
commit f643246a84
No known key found for this signature in database
2 changed files with 19 additions and 5 deletions

View file

@ -5,7 +5,7 @@ import click
from sqlalchemy.exc import IntegrityError
from sqlalchemy import select
from l4d2web.auth import hash_password
from l4d2web.auth import hash_password, validate_new_password
from l4d2web.db import session_scope
from l4d2web.models import Overlay, User
from l4d2web.services.overlay_creation import (
@ -31,8 +31,9 @@ def create_user(username: str, admin: bool) -> None:
password = os.getenv("LEFT4ME_ADMIN_PASSWORD")
if password is None:
password = click.prompt("Password", hide_input=True, confirmation_prompt=True)
if password == "":
raise click.ClickException("password must not be empty")
policy_error = validate_new_password(password)
if policy_error is not None:
raise click.ClickException(policy_error)
try:
with session_scope() as db:

View file

@ -202,7 +202,7 @@ def test_login_stamps_password_changed_at_in_session(client) -> None:
def test_create_user_cli_uses_environment_password(tmp_path, monkeypatch) -> None:
db_url = f"sqlite:///{tmp_path/'create_user.db'}"
monkeypatch.setenv("DATABASE_URL", db_url)
monkeypatch.setenv("LEFT4ME_ADMIN_PASSWORD", "secret")
monkeypatch.setenv("LEFT4ME_ADMIN_PASSWORD", "secretpw1")
app = create_app({"TESTING": True, "DATABASE_URL": db_url, "SECRET_KEY": "test"})
init_db()
@ -215,6 +215,19 @@ def test_create_user_cli_uses_environment_password(tmp_path, monkeypatch) -> Non
assert user.admin is True
def test_create_user_cli_rejects_short_password(tmp_path, monkeypatch) -> None:
db_url = f"sqlite:///{tmp_path/'short_pw.db'}"
monkeypatch.setenv("DATABASE_URL", db_url)
monkeypatch.setenv("LEFT4ME_ADMIN_PASSWORD", "short7x")
app = create_app({"TESTING": True, "DATABASE_URL": db_url, "SECRET_KEY": "test"})
init_db()
result = app.test_cli_runner().invoke(args=["create-user", "admin", "--admin"])
assert result.exit_code != 0
assert "at least 8" in result.output
def test_create_user_cli_rejects_empty_environment_password(tmp_path, monkeypatch) -> None:
db_url = f"sqlite:///{tmp_path/'empty_password.db'}"
monkeypatch.setenv("DATABASE_URL", db_url)
@ -247,7 +260,7 @@ def test_validate_new_password_accepts_min_length():
def test_create_user_cli_rejects_duplicate_username(tmp_path, monkeypatch) -> None:
db_url = f"sqlite:///{tmp_path/'duplicate_user.db'}"
monkeypatch.setenv("DATABASE_URL", db_url)
monkeypatch.setenv("LEFT4ME_ADMIN_PASSWORD", "secret")
monkeypatch.setenv("LEFT4ME_ADMIN_PASSWORD", "secretpw1")
app = create_app({"TESTING": True, "DATABASE_URL": db_url, "SECRET_KEY": "test"})
init_db()
with session_scope() as session: