fix auth: validate logout body, harden WebAuthn, optimize list_users
- Raise ValueError on missing request body in auth_logout - Add username check in verify_authentication to prevent credential reuse - Replace N+1 queries in list_users with single JOIN query
This commit is contained in:
@@ -135,7 +135,7 @@ def auth_logout(request: Any, body: Any) -> dict[str, Any]:
|
|||||||
Success response.
|
Success response.
|
||||||
"""
|
"""
|
||||||
if not body:
|
if not body:
|
||||||
return {}
|
raise ValueError("Request body is required")
|
||||||
|
|
||||||
jti = body.get("jti")
|
jti = body.get("jti")
|
||||||
if jti:
|
if jti:
|
||||||
|
|||||||
+13
-12
@@ -19,9 +19,9 @@ from lib.db import (
|
|||||||
Q_DELETE_PERMISSIONS,
|
Q_DELETE_PERMISSIONS,
|
||||||
Q_DELETE_USER,
|
Q_DELETE_USER,
|
||||||
Q_INSERT_USER,
|
Q_INSERT_USER,
|
||||||
Q_SELECT_ALL_USERS,
|
|
||||||
Q_SELECT_PERMISSIONS,
|
Q_SELECT_PERMISSIONS,
|
||||||
Q_SELECT_USER_BY_NAME,
|
Q_SELECT_USER_BY_NAME,
|
||||||
|
Q_SELECT_USERS_WITH_PERMS,
|
||||||
Q_UPDATE_PASSWORD,
|
Q_UPDATE_PASSWORD,
|
||||||
Q_UPSERT_PERMISSION,
|
Q_UPSERT_PERMISSION,
|
||||||
get_db,
|
get_db,
|
||||||
@@ -244,21 +244,22 @@ def list_users() -> list[dict[str, Any]]:
|
|||||||
List of user summary dicts.
|
List of user summary dicts.
|
||||||
"""
|
"""
|
||||||
db = get_db()
|
db = get_db()
|
||||||
rows = db.query(Q_SELECT_ALL_USERS, ())
|
rows = db.query(Q_SELECT_USERS_WITH_PERMS, ())
|
||||||
|
|
||||||
result = []
|
users: dict[int, dict[str, Any]] = {}
|
||||||
for row in rows:
|
for row in rows:
|
||||||
username = row["username"]
|
uid = row["id"]
|
||||||
permissions = _get_permissions(username)
|
if uid not in users:
|
||||||
result.append(
|
users[uid] = {
|
||||||
{
|
"id": uid,
|
||||||
"id": row["id"],
|
"username": row["username"],
|
||||||
"username": username,
|
"permissions": {},
|
||||||
"permissions": permissions,
|
|
||||||
"created_at": row["created_at"],
|
"created_at": row["created_at"],
|
||||||
}
|
}
|
||||||
)
|
if row["subsystem"] is not None:
|
||||||
return result
|
users[uid]["permissions"][row["subsystem"]] = row["level"]
|
||||||
|
|
||||||
|
return list(users.values())
|
||||||
|
|
||||||
|
|
||||||
def delete_user(username: str) -> bool:
|
def delete_user(username: str) -> bool:
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ Q_SELECT_WEBAUTHN_COUNTS = "select_webauthn_counts"
|
|||||||
Q_DELETE_WEBAUTHN = "delete_webauthn"
|
Q_DELETE_WEBAUTHN = "delete_webauthn"
|
||||||
Q_UPDATE_WEBAUTHN_SIGN_COUNT = "update_webauthn_sign_count"
|
Q_UPDATE_WEBAUTHN_SIGN_COUNT = "update_webauthn_sign_count"
|
||||||
Q_SELECT_ALL_USERS = "select_all_users"
|
Q_SELECT_ALL_USERS = "select_all_users"
|
||||||
|
Q_SELECT_USERS_WITH_PERMS = "select_users_with_perms"
|
||||||
Q_SELECT_USER_JWT_SECRET = "select_user_jwt_secret"
|
Q_SELECT_USER_JWT_SECRET = "select_user_jwt_secret"
|
||||||
Q_UPDATE_JWT_SECRET = "update_jwt_secret"
|
Q_UPDATE_JWT_SECRET = "update_jwt_secret"
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from lib.db import (
|
|||||||
Q_SELECT_USER_BY_ID,
|
Q_SELECT_USER_BY_ID,
|
||||||
Q_SELECT_USER_BY_NAME,
|
Q_SELECT_USER_BY_NAME,
|
||||||
Q_SELECT_USER_JWT_SECRET,
|
Q_SELECT_USER_JWT_SECRET,
|
||||||
|
Q_SELECT_USERS_WITH_PERMS,
|
||||||
Q_SELECT_WEBAUTHN_COUNTS,
|
Q_SELECT_WEBAUTHN_COUNTS,
|
||||||
Q_SELECT_WEBAUTHN_ID,
|
Q_SELECT_WEBAUTHN_ID,
|
||||||
Q_SELECT_WEBAUTHN_USER,
|
Q_SELECT_WEBAUTHN_USER,
|
||||||
@@ -70,6 +71,11 @@ class SQLiteBackend(Database):
|
|||||||
Q_SELECT_ALL_USERS: (
|
Q_SELECT_ALL_USERS: (
|
||||||
"SELECT id, username, created_at FROM users ORDER BY username"
|
"SELECT id, username, created_at FROM users ORDER BY username"
|
||||||
),
|
),
|
||||||
|
Q_SELECT_USERS_WITH_PERMS: (
|
||||||
|
"SELECT u.id, u.username, u.created_at, p.subsystem, p.level "
|
||||||
|
"FROM users u LEFT JOIN permissions p ON u.username = p.username "
|
||||||
|
"ORDER BY u.username"
|
||||||
|
),
|
||||||
Q_SELECT_USER_JWT_SECRET: ("SELECT jwt_secret FROM users WHERE username = ?"),
|
Q_SELECT_USER_JWT_SECRET: ("SELECT jwt_secret FROM users WHERE username = ?"),
|
||||||
Q_UPDATE_JWT_SECRET: ("UPDATE users SET jwt_secret = ? WHERE username = ?"),
|
Q_UPDATE_JWT_SECRET: ("UPDATE users SET jwt_secret = ? WHERE username = ?"),
|
||||||
# Permissions
|
# Permissions
|
||||||
|
|||||||
@@ -324,6 +324,9 @@ def verify_authentication(
|
|||||||
raise ValueError("Credential not found")
|
raise ValueError("Credential not found")
|
||||||
|
|
||||||
cred_row = cred_rows[0]
|
cred_row = cred_rows[0]
|
||||||
|
if cred_row["username"] != username:
|
||||||
|
raise ValueError("Credential not found")
|
||||||
|
|
||||||
public_key = b64u_decode(cred_row["public_key"])
|
public_key = b64u_decode(cred_row["public_key"])
|
||||||
old_sign_count = cred_row["sign_count"]
|
old_sign_count = cred_row["sign_count"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user