diff --git a/README.md b/README.md index b2c8564..5c484bc 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ The app expects a MariaDB instance configured through environment variables. - `LOGIN_WINDOW_SECONDS` (default: `300`) - `LOGIN_LOCKOUT_SECONDS` (default: `900`) - `MAX_ICON_BYTES` (default: `2097152`) +- `USERNAME_MAX_LEN` (default: `64`) +- `PASSWORD_MIN_LEN` (default: `12`) ## Gitea CI/CD diff --git a/backend/main.py b/backend/main.py index db5608e..85c08ef 100644 --- a/backend/main.py +++ b/backend/main.py @@ -33,6 +33,8 @@ MAX_DESCRIPTION_LEN = int(os.getenv("MAX_DESCRIPTION_LEN", "2000")) MAX_ICON_URL_LEN = int(os.getenv("MAX_ICON_URL_LEN", "2048")) MAX_ICON_BYTES = int(os.getenv("MAX_ICON_BYTES", str(2 * 1024 * 1024))) ALLOWED_ICON_MIME = {"image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml", "image/x-icon"} +USERNAME_MAX_LEN = int(os.getenv("USERNAME_MAX_LEN", "64")) +PASSWORD_MIN_LEN = int(os.getenv("PASSWORD_MIN_LEN", "12")) DB_HOST = os.getenv("DB_HOST", "mariadb") DB_PORT = int(os.getenv("DB_PORT", "3306")) DB_USER = os.getenv("DB_USER", "jellomator") @@ -276,6 +278,15 @@ def validate_http_url(value: str, field_name: str = "url") -> None: raise HTTPException(422, f"{field_name} must be a valid http(s) URL") +def validate_credentials(username: str, password: str) -> None: + if not username or len(username.strip()) == 0: + raise HTTPException(422, "username is required") + if len(username) > USERNAME_MAX_LEN: + raise HTTPException(422, f"username exceeds max length of {USERNAME_MAX_LEN}") + if len(password or "") < PASSWORD_MIN_LEN: + raise HTTPException(422, f"password must be at least {PASSWORD_MIN_LEN} characters") + + def validate_length(value: str | None, limit: int, field_name: str) -> None: if value is not None and len(value) > limit: raise HTTPException(422, f"{field_name} exceeds max length of {limit}") @@ -314,6 +325,7 @@ def me(request: Request, response: Response): @app.post("/api/setup") def setup(inp: SetupIn): + validate_credentials(inp.username, inp.password) with db() as c: with c.cursor() as cur: cur.execute("select count(*) as count from users") @@ -326,6 +338,7 @@ def setup(inp: SetupIn): @app.post("/api/login") def login(request: Request, inp: LoginIn): + validate_credentials(inp.username, inp.password) now_ts = time.time() prune_login_tracking(now_ts) key = login_key(request, inp.username)