In this activity, you will balance Security (stopping attackers) vs. Usability (allowing user mistakes) by implementing a lockout policy.
Scenario:
Attackers are running a "Brute Force" script against your login page, trying thousands of passwords per second. Your current code allows infinite attempts.
Your Task
Modify the check_login function to enforce these rules:
Check History: If the user has 3 or more failed attempts, return "LOCKED" immediately.
On Fail: If the password is wrong, increment the failure count and return "FAIL".
On Success: If the password is correct, reset the failure count to 0 (Usability!) and return "SUCCESS".
Why This Matters
If you don't reset the count on success, a valid user who made a typo yesterday might get locked out today. This illustrates the syllabus goal of "balancing usability and security."
PYTHON EDITOR (auth_module.py)
1
SECURITY CONSOLE OUTPUT
Ready... Waiting for login simulation.
from js import document, window
import sys
def setup():
loading = document.getElementById("loading")
if loading:
loading.style.display = "none"
setup()
class ConsoleOutput:
def write(self, text):
if text.strip():
log(text.strip(), "normal")
def flush(self):
pass
sys.stdout = ConsoleOutput()
sys.stderr = ConsoleOutput()
def log(message, type="normal"):
console = document.getElementById("console-output")
if type == "clear":
console.innerHTML = ""
return
color = "#ffffff"
if type == "pass": color = "#2ecc71"
if type == "fail": color = "#e74c3c"
if type == "info": color = "#3498db"
if type == "system": color = "#d4d4d4"
safe_message = message.replace("<", "<").replace(">", ">")
console.innerHTML += f'
{safe_message}
'
console.scrollTop = console.scrollHeight
# --- TEST RUNNER ---
def run_tests(*args):
log("", "clear")
log("[SYSTEM] Starting Authentication Simulator...", "system")
user_code = document.getElementById("code-editor").value
# Helper to create fresh user for each test run
def get_fresh_user():
return {'failed_attempts': 0}
test_env = {}
window.submissionStatus = "INCOMPLETE / FAILED"
try:
# Execute in test_env
exec(user_code, test_env)
if "check_login" not in test_env:
log("[!] Error: Function 'check_login' not found.", "fail")
return
check_login = test_env["check_login"]
# --- TEST 1: Normal Login ---
log("--- TEST 1: Valid Login ---", "system")
u1 = get_fresh_user()
res1 = check_login("secret123", "secret123", u1)
if res1 == "SUCCESS":
log(" -> PASS: Login successful.", "pass")
else:
log(f" -> FAIL: Expected 'SUCCESS', got '{res1}'", "fail")
return
# --- TEST 2: Lockout Logic ---
log("--- TEST 2: Brute Force Simulation ---", "system")
u2 = get_fresh_user()
check_login("wrong1", "secret", u2)
if u2['failed_attempts'] != 1:
log(" -> FAIL: You didn't increment failed_attempts.", "fail")
return
log(" -> Attempt 1 Failed (Count: 1)")
check_login("wrong2", "secret", u2)
log(" -> Attempt 2 Failed (Count: 2)")
check_login("wrong3", "secret", u2)
log(" -> Attempt 3 Failed (Count: 3)")
res_locked = check_login("wrong4", "secret", u2)
if res_locked == "LOCKED":
log(" -> PASS: Account successfully locked.", "pass")
else:
log(f" -> FAIL: Security hole! Expected 'LOCKED', got '{res_locked}'", "fail")
window.submissionStatus = "FAILED - Vulnerable"
return
# --- TEST 3: Usability ---
log("--- TEST 3: Usability (Reset on Success) ---", "system")
u3 = get_fresh_user()
u3['failed_attempts'] = 2
check_login("secret", "secret", u3)
if u3['failed_attempts'] == 0:
log(" -> PASS: Failure count reset to 0.", "pass")
log(" SUCCESS: Security and Usability balanced.", "pass")
window.submissionStatus = "PASSED - Secure"
else:
log(f" -> FAIL: Bad Usability! Count is {u3['failed_attempts']}.", "fail")
window.submissionStatus = "FAILED - Poor Usability"
except Exception as e:
# --- SMART HINT SYSTEM ---
# Catches the specific error for 'failed_attempts'
error_msg = str(e)
if "name 'failed_attempts' is not defined" in error_msg:
log(f"[!] HINT: 'failed_attempts' is not a variable.", "info")
log(f" It is inside the dictionary 'user_data'.", "info")
log(f" Try this: user_data['failed_attempts']", "info")
else:
log(f"[!] Runtime Error: {e}", "fail")
window.submissionStatus = "ERROR - Syntax/Runtime"