Verimed-AI / auth.py
Dipika Bhadane
changes file upload and login register
df568a8
Raw
History Blame Contribute Delete
3.96 kB
"""
Authentication module.
Real password hashing (werkzeug's generate_password_hash / check_password_hash
-- salted, uses PBKDF2/scrypt depending on werkzeug version) and Flask-Login
session management.
NOTE ON SCOPE: this is hackathon-appropriate auth -- correct password hashing,
proper session cookies via Flask-Login, but no email verification, no
password reset flow, no rate limiting on login attempts. Be upfront about
that if asked -- it's a normal, expected scope cut, not a hidden flaw.
"""
import sqlite3
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
DB_FILE = "verimed.db"
class User(UserMixin):
"""Flask-Login needs an object with .id, .is_authenticated, etc. -- UserMixin provides those."""
def __init__(self, id, name, email):
self.id = str(id)
self.name = name
self.email = email
def init_users_table():
conn = sqlite3.connect(DB_FILE)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT NOT NULL
)
""")
conn.commit()
conn.close()
def create_user(name: str, email: str, password: str) -> "User | None":
"""Returns the created User, or None if the email is already registered."""
conn = sqlite3.connect(DB_FILE)
cur = conn.cursor()
cur.execute("SELECT id FROM users WHERE email = ?", (email.lower().strip(),))
if cur.fetchone():
conn.close()
return None # email already taken
password_hash = generate_password_hash(password)
now = datetime.now().isoformat(timespec="seconds")
cur.execute(
"INSERT INTO users (name, email, password_hash, created_at) VALUES (?, ?, ?, ?)",
(name.strip(), email.lower().strip(), password_hash, now),
)
conn.commit()
user_id = cur.lastrowid
conn.close()
return User(user_id, name.strip(), email.lower().strip())
def verify_login(email: str, password: str) -> "User | None":
"""Returns the User if email+password match, else None."""
conn = sqlite3.connect(DB_FILE)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute("SELECT * FROM users WHERE email = ?", (email.lower().strip(),))
row = cur.fetchone()
conn.close()
if not row:
return None
if not check_password_hash(row["password_hash"], password):
return None
return User(row["id"], row["name"], row["email"])
def get_user_by_id(user_id: str) -> "User | None":
"""Required by Flask-Login's user_loader callback."""
conn = sqlite3.connect(DB_FILE)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute("SELECT * FROM users WHERE id = ?", (user_id,))
row = cur.fetchone()
conn.close()
if not row:
return None
return User(row["id"], row["name"], row["email"])
if __name__ == "__main__":
# Quick manual test -- run: python auth.py
import os
if os.path.exists(DB_FILE):
os.remove(DB_FILE) # clean slate for this test only
init_users_table()
user = create_user("Test User", "test@example.com", "correct-password-123")
print(f"Created user: {user.name} ({user.email}), id={user.id}")
dupe = create_user("Someone Else", "test@example.com", "whatever")
print(f"Duplicate email rejected: {dupe is None}")
good_login = verify_login("test@example.com", "correct-password-123")
print(f"Correct password login: {'SUCCESS' if good_login else 'FAILED'}")
bad_login = verify_login("test@example.com", "wrong-password")
print(f"Wrong password login: {'correctly rejected' if bad_login is None else 'SECURITY BUG'}")
fetched = get_user_by_id(user.id)
print(f"Fetched by id: {fetched.name} ({fetched.email})")