- Fix missing await on FocusGroup.get_messages() (N-L1) - Replace time.sleep with asyncio.sleep in key_theme_service and focus_group_service (N-P10) - Replace flask import with quart in focus_groups.py (N-S3) - Add logger.error before all 500 returns in focus_groups.py (N-P6) - Add logging to silent except blocks across routes (N-M10, N-M11) - Add @rate_limit to 6 remaining AI endpoints (N-H4) - Add --confirm flag to populate scripts before delete_many (S-H2) - Remove hardcoded Azure ID fallbacks from msal_service.py and msalConfig.ts (A-M2, F-H4) - Centralize make_serializable() in utils.py, remove duplicates from 3 route files (N-P7) - Replace all datetime.utcnow() with datetime.now(timezone.utc) across entire backend (M-L2) - AuthContext.tsx: only mark token validated on 200 success, not on non-401 errors (F-H2) - Rename authType → auth_type in auth.py (N-S4) - Add security_report.md and security_report.pdf with full 92-finding status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
2.4 KiB
Python
Executable file
78 lines
2.4 KiB
Python
Executable file
import bcrypt
|
|
from bson import ObjectId
|
|
from app.db import get_db
|
|
|
|
class User:
|
|
def __init__(self, username, email, password_hash=None, role="user", auth_type="local", microsoft_id=None):
|
|
self.username = username
|
|
self.email = email
|
|
self.password_hash = password_hash
|
|
self.role = role
|
|
self.auth_type = auth_type
|
|
self.microsoft_id = microsoft_id
|
|
|
|
@staticmethod
|
|
def hash_password(password):
|
|
salt = bcrypt.gensalt()
|
|
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
|
|
return hashed.decode('utf-8')
|
|
|
|
@staticmethod
|
|
def check_password(password_hash, password):
|
|
return bcrypt.checkpw(password.encode('utf-8'), password_hash.encode('utf-8'))
|
|
|
|
@staticmethod
|
|
async def find_by_username(username):
|
|
db = await get_db()
|
|
user_data = await db.users.find_one({"username": username})
|
|
return user_data
|
|
|
|
@staticmethod
|
|
async def find_by_email(email):
|
|
db = await get_db()
|
|
user_data = await db.users.find_one({"email": email})
|
|
return user_data
|
|
|
|
@staticmethod
|
|
async def find_by_id(user_id):
|
|
db = await get_db()
|
|
user_data = await db.users.find_one({"_id": ObjectId(user_id)})
|
|
return user_data
|
|
|
|
@staticmethod
|
|
async def find_by_microsoft_id(microsoft_id):
|
|
db = await get_db()
|
|
user_data = await db.users.find_one({"microsoft_id": microsoft_id})
|
|
return user_data
|
|
|
|
@staticmethod
|
|
async def update_microsoft_id(user_id, microsoft_id):
|
|
db = await get_db()
|
|
result = await db.users.update_one(
|
|
{"_id": ObjectId(user_id)},
|
|
{"$set": {"microsoft_id": microsoft_id, "auth_type": "microsoft"}}
|
|
)
|
|
return result.modified_count > 0
|
|
|
|
def to_dict(self):
|
|
return {
|
|
"username": self.username,
|
|
"email": self.email,
|
|
"role": self.role,
|
|
"auth_type": self.auth_type,
|
|
"microsoft_id": self.microsoft_id
|
|
}
|
|
|
|
async def save(self):
|
|
db = await get_db()
|
|
user_data = {
|
|
"username": self.username,
|
|
"email": self.email,
|
|
"password_hash": self.password_hash,
|
|
"role": self.role,
|
|
"auth_type": self.auth_type,
|
|
"microsoft_id": self.microsoft_id
|
|
}
|
|
result = await db.users.insert_one(user_data)
|
|
return result.inserted_id
|
|
|