Spaces:
Running on Zero
Running on Zero
Commit ·
acd778f
1
Parent(s): afd9209
feat(security): isolate only superadmin whitelist for employee access across users and db tabs
Browse files- app/auth_deps.py +0 -3
- app/superadmin_routes.py +118 -46
app/auth_deps.py
CHANGED
|
@@ -125,10 +125,7 @@ async def require_admin(
|
|
| 125 |
|
| 126 |
# Authoritative whitelist check for superadmin override
|
| 127 |
whitelist = {
|
| 128 |
-
"admin@sahihislam.com",
|
| 129 |
-
"suhaileng.bhat@gmail.com",
|
| 130 |
"zainshafibhat@gmail.com",
|
| 131 |
-
"zainshafibhat2@gmail.com",
|
| 132 |
}
|
| 133 |
env_emails = os.getenv("SUPERADMIN_EMAILS", "")
|
| 134 |
for e in env_emails.split(","):
|
|
|
|
| 125 |
|
| 126 |
# Authoritative whitelist check for superadmin override
|
| 127 |
whitelist = {
|
|
|
|
|
|
|
| 128 |
"zainshafibhat@gmail.com",
|
|
|
|
| 129 |
}
|
| 130 |
env_emails = os.getenv("SUPERADMIN_EMAILS", "")
|
| 131 |
for e in env_emails.split(","):
|
app/superadmin_routes.py
CHANGED
|
@@ -59,10 +59,7 @@ router = APIRouter()
|
|
| 59 |
# SUPERADMIN ACCESS CONTROL (ENTERPRISE DEFENSE-IN-DEPTH)
|
| 60 |
# =========================================================
|
| 61 |
SUPERADMIN_IMMUTABLE_WHITELIST = {
|
| 62 |
-
"admin@sahihislam.com",
|
| 63 |
-
"suhaileng.bhat@gmail.com",
|
| 64 |
"zainshafibhat@gmail.com",
|
| 65 |
-
"zainshafibhat2@gmail.com",
|
| 66 |
}
|
| 67 |
|
| 68 |
PROTECTED_SECURITY_ATTRIBUTES = {
|
|
@@ -539,24 +536,22 @@ async def get_all_users(
|
|
| 539 |
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 540 |
|
| 541 |
# 1. SECURITY PRIVACY FILTER:
|
| 542 |
-
# If caller is an employee (not true SuperAdmin), filter out
|
| 543 |
-
#
|
| 544 |
# The SuperAdmin caller sees EVERY single user in the entire system with zero exclusions.
|
| 545 |
if not is_sa_caller:
|
| 546 |
protected_emails = set(whitelist)
|
| 547 |
-
|
| 548 |
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
protected_uids.add(s["uid"].strip())
|
| 554 |
|
| 555 |
# Forbid employee from filtering by SUPERADMIN role
|
| 556 |
if role and role.strip().upper() == "SUPERADMIN":
|
| 557 |
return []
|
| 558 |
|
| 559 |
-
protected_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in protected_emails]
|
| 560 |
exclusion_clauses = [{"email": {"$nin": protected_regexes}}]
|
| 561 |
if protected_uids:
|
| 562 |
exclusion_clauses.append({"uid": {"$nin": list(protected_uids)}})
|
|
@@ -713,13 +708,10 @@ async def update_user_attributes(
|
|
| 713 |
if not is_sa_caller:
|
| 714 |
target_email = (target.get("email") or "").strip().lower()
|
| 715 |
whitelist = get_superadmin_whitelist()
|
| 716 |
-
|
| 717 |
-
"$or": [{"uid": uid}, {"email": target_email}]
|
| 718 |
-
})
|
| 719 |
-
if target_email in whitelist or is_staff_target:
|
| 720 |
raise HTTPException(
|
| 721 |
status_code=403,
|
| 722 |
-
detail="Access denied:
|
| 723 |
)
|
| 724 |
|
| 725 |
update_data = {}
|
|
@@ -831,13 +823,10 @@ async def delete_user(uid: str, admin: dict = Depends(require_superadmin)):
|
|
| 831 |
if not is_sa_caller:
|
| 832 |
target_email = (target.get("email") or "").strip().lower()
|
| 833 |
whitelist = get_superadmin_whitelist()
|
| 834 |
-
|
| 835 |
-
"$or": [{"uid": uid}, {"email": target_email}]
|
| 836 |
-
})
|
| 837 |
-
if target_email in whitelist or is_staff_target:
|
| 838 |
raise HTTPException(
|
| 839 |
status_code=403,
|
| 840 |
-
detail="Access denied:
|
| 841 |
)
|
| 842 |
|
| 843 |
# Delete user document
|
|
@@ -890,10 +879,7 @@ async def search_user_by_email(
|
|
| 890 |
if not is_sa_caller:
|
| 891 |
target_email = (user.get("email") or "").strip().lower()
|
| 892 |
whitelist = get_superadmin_whitelist()
|
| 893 |
-
|
| 894 |
-
"$or": [{"uid": user.get("uid")}, {"email": target_email}]
|
| 895 |
-
})
|
| 896 |
-
if target_email in whitelist or is_staff_target:
|
| 897 |
raise HTTPException(status_code=404, detail=f"No user found with email matching '{clean_email}'")
|
| 898 |
|
| 899 |
# Concurrent fetch of wallet balance & recent VIP transactions for user
|
|
@@ -2130,18 +2116,19 @@ async def get_statistics(admin: dict = Depends(require_superadmin)):
|
|
| 2130 |
"""
|
| 2131 |
t0 = time.perf_counter()
|
| 2132 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2133 |
async def _count_users():
|
|
|
|
|
|
|
| 2134 |
return await users_collection.count_documents({})
|
| 2135 |
|
| 2136 |
async def _count_superadmins():
|
| 2137 |
-
|
| 2138 |
-
|
| 2139 |
-
|
| 2140 |
-
{"auth.superadmin": True},
|
| 2141 |
-
{"auth.is_superadmin": True},
|
| 2142 |
-
{"role": "SUPERADMIN"}
|
| 2143 |
-
]
|
| 2144 |
-
})
|
| 2145 |
|
| 2146 |
async def _count_pro_users():
|
| 2147 |
return await users_collection.count_documents({
|
|
@@ -2341,6 +2328,10 @@ async def get_database_overview(admin: dict = Depends(require_superadmin)):
|
|
| 2341 |
("sahihislam_commerce", "Commerce & Ads", db_commerce, "financial"),
|
| 2342 |
]
|
| 2343 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2344 |
db_summaries = []
|
| 2345 |
overall_total_docs = 0
|
| 2346 |
overall_total_collections = 0
|
|
@@ -2349,18 +2340,30 @@ async def get_database_overview(admin: dict = Depends(require_superadmin)):
|
|
| 2349 |
coll_names = await db_handle.list_collection_names()
|
| 2350 |
filtered_colls = sorted([c for c in coll_names if not c.startswith("system.")])
|
| 2351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2352 |
async def _inspect_collection(c_name: str):
|
| 2353 |
coll = db_handle[c_name]
|
| 2354 |
try:
|
| 2355 |
-
|
| 2356 |
-
|
| 2357 |
-
|
| 2358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2359 |
except Exception:
|
| 2360 |
try:
|
| 2361 |
-
|
|
|
|
|
|
|
|
|
|
| 2362 |
except Exception:
|
| 2363 |
-
doc_count =
|
| 2364 |
size_bytes = 0
|
| 2365 |
index_count = 1
|
| 2366 |
return {
|
|
@@ -2426,7 +2429,30 @@ async def get_collection_documents(
|
|
| 2426 |
db_handle, db_name = _get_target_database(database if isinstance(database, str) else None)
|
| 2427 |
coll = db_handle[collection]
|
| 2428 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2429 |
query_parts = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2430 |
|
| 2431 |
# 1. Apply raw JSON filter if provided
|
| 2432 |
if isinstance(filter_json, str) and filter_json.strip():
|
|
@@ -2572,9 +2598,19 @@ async def get_single_document(
|
|
| 2572 |
try:
|
| 2573 |
db_handle, db_name = _get_target_database(database)
|
| 2574 |
coll = db_handle[collection]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2575 |
doc = await coll.find_one(_get_doc_id_query(doc_id))
|
| 2576 |
if not doc:
|
| 2577 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2578 |
return {
|
| 2579 |
"database": db_name,
|
| 2580 |
"collection": collection,
|
|
@@ -2672,6 +2708,16 @@ async def update_document(
|
|
| 2672 |
if not existing:
|
| 2673 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2674 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2675 |
update_fields = dict(payload)
|
| 2676 |
if "_id" in update_fields:
|
| 2677 |
del update_fields["_id"]
|
|
@@ -2737,6 +2783,16 @@ async def delete_document(
|
|
| 2737 |
if not existing:
|
| 2738 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2739 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2740 |
# Safeguard 1: Prevent self-deletion of superadmin user
|
| 2741 |
if collection == "users":
|
| 2742 |
target_uid = existing.get("uid")
|
|
@@ -2800,6 +2856,22 @@ async def execute_query(
|
|
| 2800 |
if not collection_name:
|
| 2801 |
raise HTTPException(status_code=400, detail="Collection name required")
|
| 2802 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2803 |
coll = db_handle[collection_name]
|
| 2804 |
|
| 2805 |
if operation == "count":
|
|
@@ -2823,11 +2895,6 @@ async def execute_query(
|
|
| 2823 |
except Exception as e:
|
| 2824 |
logger.error(f"SuperAdmin query error: {str(e)}")
|
| 2825 |
raise HTTPException(status_code=500, detail=f"Query failed: {str(e)}")
|
| 2826 |
-
except HTTPException:
|
| 2827 |
-
raise
|
| 2828 |
-
except Exception as e:
|
| 2829 |
-
logger.error(f"SuperAdmin query error: {str(e)}")
|
| 2830 |
-
raise HTTPException(status_code=500, detail=f"Query failed: {str(e)}")
|
| 2831 |
|
| 2832 |
|
| 2833 |
# =========================================================
|
|
@@ -5533,8 +5600,13 @@ async def simulate_search_tier_query(
|
|
| 5533 |
async def get_database_stats(admin: dict = Depends(require_superadmin)):
|
| 5534 |
"""Get database collection statistics."""
|
| 5535 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5536 |
stats = {
|
| 5537 |
-
"users_count": await users_collection.count_documents(
|
| 5538 |
"masjids_count": await masjid_collection.count_documents({}),
|
| 5539 |
"hadiths_count": await hadith_collection.count_documents({}),
|
| 5540 |
"ads_count": await ads_collection.count_documents({}),
|
|
|
|
| 59 |
# SUPERADMIN ACCESS CONTROL (ENTERPRISE DEFENSE-IN-DEPTH)
|
| 60 |
# =========================================================
|
| 61 |
SUPERADMIN_IMMUTABLE_WHITELIST = {
|
|
|
|
|
|
|
| 62 |
"zainshafibhat@gmail.com",
|
|
|
|
| 63 |
}
|
| 64 |
|
| 65 |
PROTECTED_SECURITY_ATTRIBUTES = {
|
|
|
|
| 536 |
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 537 |
|
| 538 |
# 1. SECURITY PRIVACY FILTER:
|
| 539 |
+
# If caller is an employee (not true SuperAdmin), filter out ONLY the SuperAdmin account(s).
|
| 540 |
+
# Regular users and fellow accounts remain visible to the employee.
|
| 541 |
# The SuperAdmin caller sees EVERY single user in the entire system with zero exclusions.
|
| 542 |
if not is_sa_caller:
|
| 543 |
protected_emails = set(whitelist)
|
| 544 |
+
protected_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in protected_emails]
|
| 545 |
|
| 546 |
+
protected_uids = set()
|
| 547 |
+
async for u in users_collection.find({"email": {"$in": protected_regexes}}, {"uid": 1}):
|
| 548 |
+
if u.get("uid"):
|
| 549 |
+
protected_uids.add(u["uid"].strip())
|
|
|
|
| 550 |
|
| 551 |
# Forbid employee from filtering by SUPERADMIN role
|
| 552 |
if role and role.strip().upper() == "SUPERADMIN":
|
| 553 |
return []
|
| 554 |
|
|
|
|
| 555 |
exclusion_clauses = [{"email": {"$nin": protected_regexes}}]
|
| 556 |
if protected_uids:
|
| 557 |
exclusion_clauses.append({"uid": {"$nin": list(protected_uids)}})
|
|
|
|
| 708 |
if not is_sa_caller:
|
| 709 |
target_email = (target.get("email") or "").strip().lower()
|
| 710 |
whitelist = get_superadmin_whitelist()
|
| 711 |
+
if target_email in whitelist:
|
|
|
|
|
|
|
|
|
|
| 712 |
raise HTTPException(
|
| 713 |
status_code=403,
|
| 714 |
+
detail="Access denied: SuperAdmin accounts cannot be modified by employees."
|
| 715 |
)
|
| 716 |
|
| 717 |
update_data = {}
|
|
|
|
| 823 |
if not is_sa_caller:
|
| 824 |
target_email = (target.get("email") or "").strip().lower()
|
| 825 |
whitelist = get_superadmin_whitelist()
|
| 826 |
+
if target_email in whitelist:
|
|
|
|
|
|
|
|
|
|
| 827 |
raise HTTPException(
|
| 828 |
status_code=403,
|
| 829 |
+
detail="Access denied: SuperAdmin accounts cannot be deleted by employees."
|
| 830 |
)
|
| 831 |
|
| 832 |
# Delete user document
|
|
|
|
| 879 |
if not is_sa_caller:
|
| 880 |
target_email = (user.get("email") or "").strip().lower()
|
| 881 |
whitelist = get_superadmin_whitelist()
|
| 882 |
+
if target_email in whitelist:
|
|
|
|
|
|
|
|
|
|
| 883 |
raise HTTPException(status_code=404, detail=f"No user found with email matching '{clean_email}'")
|
| 884 |
|
| 885 |
# Concurrent fetch of wallet balance & recent VIP transactions for user
|
|
|
|
| 2116 |
"""
|
| 2117 |
t0 = time.perf_counter()
|
| 2118 |
try:
|
| 2119 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2120 |
+
whitelist = get_superadmin_whitelist()
|
| 2121 |
+
whitelist_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in whitelist]
|
| 2122 |
+
|
| 2123 |
async def _count_users():
|
| 2124 |
+
if not is_sa_caller:
|
| 2125 |
+
return await users_collection.count_documents({"email": {"$nin": whitelist_regexes}})
|
| 2126 |
return await users_collection.count_documents({})
|
| 2127 |
|
| 2128 |
async def _count_superadmins():
|
| 2129 |
+
if not is_sa_caller:
|
| 2130 |
+
return 0
|
| 2131 |
+
return len(whitelist)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2132 |
|
| 2133 |
async def _count_pro_users():
|
| 2134 |
return await users_collection.count_documents({
|
|
|
|
| 2328 |
("sahihislam_commerce", "Commerce & Ads", db_commerce, "financial"),
|
| 2329 |
]
|
| 2330 |
|
| 2331 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2332 |
+
whitelist = get_superadmin_whitelist()
|
| 2333 |
+
whitelist_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in whitelist]
|
| 2334 |
+
|
| 2335 |
db_summaries = []
|
| 2336 |
overall_total_docs = 0
|
| 2337 |
overall_total_collections = 0
|
|
|
|
| 2340 |
coll_names = await db_handle.list_collection_names()
|
| 2341 |
filtered_colls = sorted([c for c in coll_names if not c.startswith("system.")])
|
| 2342 |
|
| 2343 |
+
# Hide staff_permissions collection from employees
|
| 2344 |
+
if not is_sa_caller and name == "sahihislam_admin":
|
| 2345 |
+
filtered_colls = [c for c in filtered_colls if c != "staff_permissions"]
|
| 2346 |
+
|
| 2347 |
async def _inspect_collection(c_name: str):
|
| 2348 |
coll = db_handle[c_name]
|
| 2349 |
try:
|
| 2350 |
+
if not is_sa_caller and c_name == "users":
|
| 2351 |
+
doc_count = await coll.count_documents({"email": {"$nin": whitelist_regexes}})
|
| 2352 |
+
size_bytes = 0
|
| 2353 |
+
index_count = 1
|
| 2354 |
+
else:
|
| 2355 |
+
stats = await db_handle.command("collStats", c_name)
|
| 2356 |
+
doc_count = stats.get("count", 0)
|
| 2357 |
+
size_bytes = stats.get("size", 0)
|
| 2358 |
+
index_count = stats.get("nindexes", 1)
|
| 2359 |
except Exception:
|
| 2360 |
try:
|
| 2361 |
+
if not is_sa_caller and c_name == "users":
|
| 2362 |
+
doc_count = await coll.count_documents({"email": {"$nin": whitelist_regexes}})
|
| 2363 |
+
else:
|
| 2364 |
+
doc_count = await coll.count_documents({})
|
| 2365 |
except Exception:
|
| 2366 |
+
doc_count = 0
|
| 2367 |
size_bytes = 0
|
| 2368 |
index_count = 1
|
| 2369 |
return {
|
|
|
|
| 2429 |
db_handle, db_name = _get_target_database(database if isinstance(database, str) else None)
|
| 2430 |
coll = db_handle[collection]
|
| 2431 |
|
| 2432 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2433 |
+
whitelist = get_superadmin_whitelist()
|
| 2434 |
+
whitelist_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in whitelist]
|
| 2435 |
+
|
| 2436 |
+
# Security restriction: staff_permissions collection is restricted to master SuperAdmin only
|
| 2437 |
+
if not is_sa_caller and collection == "staff_permissions":
|
| 2438 |
+
raise HTTPException(
|
| 2439 |
+
status_code=403,
|
| 2440 |
+
detail="Access denied: staff permissions collection is restricted to master SuperAdmin."
|
| 2441 |
+
)
|
| 2442 |
+
|
| 2443 |
query_parts = []
|
| 2444 |
+
|
| 2445 |
+
# Security isolation: In users collection, filter out SuperAdmin accounts for employee callers
|
| 2446 |
+
if not is_sa_caller and collection == "users":
|
| 2447 |
+
sa_uids = set()
|
| 2448 |
+
async for u in users_collection.find({"email": {"$in": whitelist_regexes}}, {"uid": 1}):
|
| 2449 |
+
if u.get("uid"):
|
| 2450 |
+
sa_uids.add(u["uid"].strip())
|
| 2451 |
+
|
| 2452 |
+
query_parts.append({"email": {"$nin": whitelist_regexes}})
|
| 2453 |
+
if sa_uids:
|
| 2454 |
+
query_parts.append({"uid": {"$nin": list(sa_uids)}})
|
| 2455 |
+
query_parts.append({"_id": {"$nin": list(sa_uids)}})
|
| 2456 |
|
| 2457 |
# 1. Apply raw JSON filter if provided
|
| 2458 |
if isinstance(filter_json, str) and filter_json.strip():
|
|
|
|
| 2598 |
try:
|
| 2599 |
db_handle, db_name = _get_target_database(database)
|
| 2600 |
coll = db_handle[collection]
|
| 2601 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2602 |
+
if not is_sa_caller and collection == "staff_permissions":
|
| 2603 |
+
raise HTTPException(status_code=403, detail="Access denied.")
|
| 2604 |
+
|
| 2605 |
doc = await coll.find_one(_get_doc_id_query(doc_id))
|
| 2606 |
if not doc:
|
| 2607 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2608 |
+
|
| 2609 |
+
if not is_sa_caller and collection == "users":
|
| 2610 |
+
target_email = (doc.get("email") or "").strip().lower()
|
| 2611 |
+
whitelist = get_superadmin_whitelist()
|
| 2612 |
+
if target_email in whitelist:
|
| 2613 |
+
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2614 |
return {
|
| 2615 |
"database": db_name,
|
| 2616 |
"collection": collection,
|
|
|
|
| 2708 |
if not existing:
|
| 2709 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2710 |
|
| 2711 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2712 |
+
if not is_sa_caller:
|
| 2713 |
+
if collection == "staff_permissions":
|
| 2714 |
+
raise HTTPException(status_code=403, detail="Forbidden: Staff permissions cannot be altered via generic DB tools.")
|
| 2715 |
+
if collection == "users":
|
| 2716 |
+
target_email = (existing.get("email") or "").strip().lower()
|
| 2717 |
+
whitelist = get_superadmin_whitelist()
|
| 2718 |
+
if target_email in whitelist:
|
| 2719 |
+
raise HTTPException(status_code=403, detail="Forbidden: SuperAdmin records cannot be modified.")
|
| 2720 |
+
|
| 2721 |
update_fields = dict(payload)
|
| 2722 |
if "_id" in update_fields:
|
| 2723 |
del update_fields["_id"]
|
|
|
|
| 2783 |
if not existing:
|
| 2784 |
raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found in {db_name}.{collection}")
|
| 2785 |
|
| 2786 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2787 |
+
if not is_sa_caller:
|
| 2788 |
+
if collection == "staff_permissions":
|
| 2789 |
+
raise HTTPException(status_code=403, detail="Forbidden: Staff permissions cannot be altered via generic DB tools.")
|
| 2790 |
+
if collection == "users":
|
| 2791 |
+
target_email = (existing.get("email") or "").strip().lower()
|
| 2792 |
+
whitelist = get_superadmin_whitelist()
|
| 2793 |
+
if target_email in whitelist:
|
| 2794 |
+
raise HTTPException(status_code=403, detail="Forbidden: SuperAdmin records cannot be deleted.")
|
| 2795 |
+
|
| 2796 |
# Safeguard 1: Prevent self-deletion of superadmin user
|
| 2797 |
if collection == "users":
|
| 2798 |
target_uid = existing.get("uid")
|
|
|
|
| 2856 |
if not collection_name:
|
| 2857 |
raise HTTPException(status_code=400, detail="Collection name required")
|
| 2858 |
|
| 2859 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 2860 |
+
if not is_sa_caller:
|
| 2861 |
+
if collection_name == "staff_permissions":
|
| 2862 |
+
raise HTTPException(status_code=403, detail="Access denied: staff permissions collection is restricted to master SuperAdmin.")
|
| 2863 |
+
if collection_name == "users":
|
| 2864 |
+
whitelist = get_superadmin_whitelist()
|
| 2865 |
+
whitelist_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in whitelist]
|
| 2866 |
+
sa_exclusion = {"email": {"$nin": whitelist_regexes}}
|
| 2867 |
+
if isinstance(query, dict):
|
| 2868 |
+
if "$and" in query:
|
| 2869 |
+
query["$and"].append(sa_exclusion)
|
| 2870 |
+
elif query:
|
| 2871 |
+
query = {"$and": [query, sa_exclusion]}
|
| 2872 |
+
else:
|
| 2873 |
+
query = sa_exclusion
|
| 2874 |
+
|
| 2875 |
coll = db_handle[collection_name]
|
| 2876 |
|
| 2877 |
if operation == "count":
|
|
|
|
| 2895 |
except Exception as e:
|
| 2896 |
logger.error(f"SuperAdmin query error: {str(e)}")
|
| 2897 |
raise HTTPException(status_code=500, detail=f"Query failed: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2898 |
|
| 2899 |
|
| 2900 |
# =========================================================
|
|
|
|
| 5600 |
async def get_database_stats(admin: dict = Depends(require_superadmin)):
|
| 5601 |
"""Get database collection statistics."""
|
| 5602 |
try:
|
| 5603 |
+
is_sa_caller = bool(admin.get("is_superadmin", False))
|
| 5604 |
+
whitelist = get_superadmin_whitelist()
|
| 5605 |
+
whitelist_regexes = [re.compile(f"^{re.escape(e)}$", re.IGNORECASE) for e in whitelist]
|
| 5606 |
+
user_query = {} if is_sa_caller else {"email": {"$nin": whitelist_regexes}}
|
| 5607 |
+
|
| 5608 |
stats = {
|
| 5609 |
+
"users_count": await users_collection.count_documents(user_query),
|
| 5610 |
"masjids_count": await masjid_collection.count_documents({}),
|
| 5611 |
"hadiths_count": await hadith_collection.count_documents({}),
|
| 5612 |
"ads_count": await ads_collection.count_documents({}),
|