4646from app .datamgmt .manage .manage_users_db import create_user
4747from app .datamgmt .manage .manage_users_db import update_user_groups
4848from app .datamgmt .manage .manage_users_db import get_user
49- from app .forms import LoginForm , MFASetupForm
49+ from app .forms import LoginForm , MFASetupForm , MFAVerifyForm
5050from app .blueprints .iris_user import iris_current_user
5151from app .iris_engine .utils .tracker import track_activity
5252from app .datamgmt .manage .manage_groups_db import get_groups_list
@@ -154,8 +154,7 @@ def login():
154154
155155 form = LoginForm (request .form )
156156
157- # check if both http method is POST and form is valid on submit
158- if not form .is_submitted () and not form .validate ():
157+ if not form .validate_on_submit ():
159158 return _render_template_login (form , None )
160159
161160 # assign form data to variables
@@ -406,20 +405,27 @@ def _clear_pre_mfa_state(preserve_lockout=False):
406405 session .pop ("pending_mfa_secret" , None )
407406 if not preserve_lockout :
408407 session .pop ("mfa_fail_count" , None )
408+ session .pop ("mfa_fail_user_id" , None )
409409 session .pop ("mfa_lockout_until" , None )
410410
411411
412- def _mfa_is_locked_out ():
412+ def _mfa_is_locked_out (user ):
413+ if session .get ("mfa_fail_user_id" ) != user .id :
414+ return False
413415 locked_until = session .get ("mfa_lockout_until" )
414416 if locked_until and locked_until > time .time ():
415417 return True
416418 if locked_until and locked_until <= time .time ():
417419 session .pop ("mfa_lockout_until" , None )
420+ session .pop ("mfa_fail_user_id" , None )
418421 session ["mfa_fail_count" ] = 0
419422 return False
420423
421424
422425def _register_mfa_failure (user , reason ):
426+ if session .get ("mfa_fail_user_id" ) != user .id :
427+ session ["mfa_fail_count" ] = 0
428+ session ["mfa_fail_user_id" ] = user .id
423429 session ["mfa_fail_count" ] = session .get ("mfa_fail_count" , 0 ) + 1
424430 track_activity (
425431 f"Failed MFA { reason } for user { user .user } "
@@ -433,6 +439,8 @@ def _register_mfa_failure(user, reason):
433439 # the lockout timestamp so a fresh /login cannot wipe it.
434440 _clear_pre_mfa_state (preserve_lockout = True )
435441 session .pop ("username" , None )
442+ return True
443+ return False
436444
437445
438446@app .route ("/auth/mfa-setup" , methods = ["GET" , "POST" ])
@@ -449,13 +457,13 @@ def mfa_setup():
449457 if user .mfa_setup_complete and user .mfa_secrets :
450458 return redirect (url_for ("mfa_verify" ))
451459
452- if _mfa_is_locked_out ():
460+ if _mfa_is_locked_out (user ):
453461 flash ("Too many attempts. Please try again later." , "danger" )
454462 return redirect (url_for ("login.login" ))
455463
456464 form = MFASetupForm ()
457465
458- if form .submit () and form . validate ():
466+ if form .validate_on_submit ():
459467
460468 token = form .token .data
461469 user_password = form .user_password .data
@@ -469,7 +477,7 @@ def mfa_setup():
469477
470478 totp = pyotp .TOTP (mfa_secret )
471479
472- if totp .verify (token ):
480+ if totp .verify (str ( token ). strip (), valid_window = 1 ):
473481 has_valid_password = False
474482 if is_authentication_ldap () is True :
475483 if validate_ldap_login (
@@ -483,7 +491,10 @@ def mfa_setup():
483491 has_valid_password = True
484492
485493 if not has_valid_password :
486- _register_mfa_failure (user , "setup (invalid password)" )
494+ locked_out = _register_mfa_failure (user , "setup (invalid password)" )
495+ if locked_out :
496+ flash ("Too many attempts. Please try again later." , "danger" )
497+ return redirect (url_for ("login.login" ))
487498 flash ("Invalid password. Please try again." , "danger" )
488499 return render_template ("mfa_setup.html" , form = form )
489500
@@ -501,7 +512,10 @@ def mfa_setup():
501512 session ["mfa_verified_for_user_id" ] = user .id
502513 _clear_pre_mfa_state ()
503514 return wrap_login_user (user )
504- _register_mfa_failure (user , "setup (invalid token)" )
515+ locked_out = _register_mfa_failure (user , "setup (invalid token)" )
516+ if locked_out :
517+ flash ("Too many attempts. Please try again later." , "danger" )
518+ return redirect (url_for ("login.login" ))
505519 flash ("Invalid token or password. Please try again." , "danger" )
506520
507521 # Generate a fresh secret on every GET and stash it in the session. The
@@ -536,21 +550,16 @@ def mfa_verify():
536550 )
537551 return redirect (url_for ("mfa_setup" ))
538552
539- if _mfa_is_locked_out ():
553+ if _mfa_is_locked_out (user ):
540554 flash ("Too many attempts. Please try again later." , "danger" )
541555 return redirect (url_for ("login.login" ))
542556
543- form = MFASetupForm ()
544- form .user_password .data = "not required for verification"
557+ form = MFAVerifyForm ()
545558
546- if form .submit () and form . validate ():
559+ if form .validate_on_submit ():
547560 token = form .token .data
548- if not token :
549- flash ("Token is required." , "danger" )
550- return render_template ("mfa_verify.html" , form = form )
551-
552561 totp = pyotp .TOTP (user .mfa_secrets )
553- if totp .verify (token ):
562+ if totp .verify (str ( token ). strip (), valid_window = 1 ):
554563 track_activity (
555564 f"MFA verification successful for user { user .user } " ,
556565 ctx_less = True , display_in_ui = False ,
@@ -561,7 +570,10 @@ def mfa_verify():
561570 session ["mfa_verified_for_user_id" ] = user .id
562571 _clear_pre_mfa_state ()
563572 return wrap_login_user (user )
564- _register_mfa_failure (user , "verification (invalid token)" )
573+ locked_out = _register_mfa_failure (user , "verification (invalid token)" )
574+ if locked_out :
575+ flash ("Too many attempts. Please try again later." , "danger" )
576+ return redirect (url_for ("login.login" ))
565577 flash ("Invalid token. Please try again." , "danger" )
566578
567579 return render_template ("mfa_verify.html" , form = form )
0 commit comments