Add security hardening and additional auth tests
Security improvements: - Add password strength validation (min 8 characters) - Invalidate all sessions on password change/reset - Add timing-safe user lookup to prevent enumeration attacks - Fix SQLAlchemy boolean comparisons (== True -> is_(True)) - Change default admin password to 'changeme123' (meets min length) New tests (7 additional): - Inactive user login attempt blocked - Short password rejected on create/change/reset - Duplicate username rejected (409) - Non-owner API key deletion blocked (403) - Sessions invalidated on password change
This commit is contained in:
@@ -368,6 +368,9 @@ from .auth import (
|
||||
get_auth_service,
|
||||
SESSION_COOKIE_NAME,
|
||||
verify_password,
|
||||
validate_password_strength,
|
||||
PasswordTooShortError,
|
||||
MIN_PASSWORD_LENGTH,
|
||||
)
|
||||
|
||||
|
||||
@@ -491,8 +494,14 @@ def change_password(
|
||||
detail="Current password is incorrect",
|
||||
)
|
||||
|
||||
# Change password
|
||||
auth_service.change_password(current_user, password_request.new_password)
|
||||
# Validate and change password
|
||||
try:
|
||||
auth_service.change_password(current_user, password_request.new_password)
|
||||
except PasswordTooShortError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Password must be at least {MIN_PASSWORD_LENGTH} characters",
|
||||
)
|
||||
|
||||
# Log audit
|
||||
_log_audit(
|
||||
@@ -659,6 +668,15 @@ def create_user(
|
||||
detail="Username already exists",
|
||||
)
|
||||
|
||||
# Validate password strength
|
||||
try:
|
||||
validate_password_strength(user_create.password)
|
||||
except PasswordTooShortError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Password must be at least {MIN_PASSWORD_LENGTH} characters",
|
||||
)
|
||||
|
||||
user = auth_service.create_user(
|
||||
username=user_create.username,
|
||||
password=user_create.password,
|
||||
@@ -738,7 +756,7 @@ def update_user(
|
||||
if user_update.is_admin is False and user.is_admin:
|
||||
admin_count = (
|
||||
auth_service.db.query(User)
|
||||
.filter(User.is_admin == True, User.is_active == True)
|
||||
.filter(User.is_admin.is_(True), User.is_active.is_(True))
|
||||
.count()
|
||||
)
|
||||
if admin_count <= 1:
|
||||
@@ -798,7 +816,13 @@ def reset_user_password(
|
||||
detail="User not found",
|
||||
)
|
||||
|
||||
auth_service.reset_user_password(user, reset_request.new_password)
|
||||
try:
|
||||
auth_service.reset_user_password(user, reset_request.new_password)
|
||||
except PasswordTooShortError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Password must be at least {MIN_PASSWORD_LENGTH} characters",
|
||||
)
|
||||
|
||||
# Log audit
|
||||
_log_audit(
|
||||
|
||||
Reference in New Issue
Block a user