aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/hub_settings.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/hub_settings.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/hub_settings.py31
1 files changed, 31 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/hub_settings.py b/packages/meshbay-hub/src/meshbay_hub/hub_settings.py
index 1c2d2c4..1dcff84 100644
--- a/packages/meshbay-hub/src/meshbay_hub/hub_settings.py
+++ b/packages/meshbay-hub/src/meshbay_hub/hub_settings.py
@@ -85,6 +85,37 @@ async def mail_limits(db: AsyncSession) -> dict[str, int]:
return {k: await get_int(db, f"mail.{k}", mail_default(k)) for k in MAIL_KEYS}
+# ── Sign-in lockout ──────────────────────────────────────────────────────────
+#
+# After `max_failures` wrong passphrases for one username, sign-in with a
+# passphrase is refused for `lockout_minutes` (`login_throttle.py`). Zero
+# failures turns the lockout off; a zero-minute lockout would be the same thing
+# said less clearly, so the duration starts at one.
+
+LOGIN_KEYS = ("max_failures", "lockout_minutes")
+
+LOGIN_DEFAULTS: dict[str, int] = {
+ "max_failures": 4,
+ "lockout_minutes": 60,
+}
+
+LOGIN_BOUNDS: dict[str, tuple[int, int]] = {
+ "max_failures": (0, 100),
+ "lockout_minutes": (1, 10_080), # a week
+}
+
+
+def clamp_login_value(key: str, value: int) -> int:
+ low, high = LOGIN_BOUNDS[key]
+ return max(low, min(high, int(value)))
+
+
+async def login_limits(db: AsyncSession) -> dict[str, int]:
+ """Both lockout numbers, stored value or built-in default."""
+ return {k: clamp_login_value(k, await get_int(db, f"login.{k}", LOGIN_DEFAULTS[k]))
+ for k in LOGIN_KEYS}
+
+
async def get_raw(db: AsyncSession, key: str) -> str | None:
row = await db.get(HubSetting, key)
return row.value if row else None