-
Notifications
You must be signed in to change notification settings - Fork 4
Add user management test file #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from fastapi import APIRouter, HTTPException, status, Depends | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Dict, Any, List | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.auth.firebase_auth import firebase_auth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.auth.dependencies import get_current_user, require_role, get_current_active_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.auth.models import UserSignupRequest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router = APIRouter(prefix="/users", tags=["user-management"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_cache: Dict[str, Dict[str, Any]] = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.post("/create-admin") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def create_admin_user(user_data: UserSignupRequest): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The admin creation endpoint is not protected by any authorization check, so any unauthenticated caller can create new admin users, which is a critical security flaw. The function should require an authenticated user and explicitly verify that the caller has an admin role before proceeding to create an admin account. [security] Severity Level: Critical 🚨
Suggested change
Why it matters? ⭐This is a real security issue: the endpoint has no Depends() or authorization check, so any caller can create admin accounts. The proposed change adds a current_user dependency and an explicit admin-role check which directly fixes the vulnerability. The improvement is appropriate for the PR's code (test_user_management uses an in-memory cache and already imports get_current_user), and it prevents privilege escalation. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/test_user_management.py
**Line:** 12:12
**Comment:**
*Security: The admin creation endpoint is not protected by any authorization check, so any unauthenticated caller can create new admin users, which is a critical security flaw. The function should require an authenticated user and explicitly verify that the caller has an admin role before proceeding to create an admin account.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Comment on lines
+11
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Authorization ControlsAdmin creation endpoint lacks authorization checks allowing any user to create admin accounts. Attackers can exploit this to escalate privileges and gain administrative access. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Creating admin with password: {user_data.password}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Password Logging ExposurePassword is logged in plaintext during admin creation, exposing sensitive credentials in application logs. Attackers with log access could retrieve admin passwords for unauthorized access. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = await firebase_auth.create_user( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email=user_data.email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| password=user_data.password, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_name=user_data.first_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_name=user_data.last_name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user["role"] = "admin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_cache[user["id"]] = user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"message": "Admin created", "user": user} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+11
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
You will need to add
Suggested change
Comment on lines
+11
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's locate and examine the test_user_management.py file
find . -name "test_user_management.py" -type fRepository: coderabbit-test/pr-reviewer Length of output: 100 🏁 Script executed: # Also check the directory structure to understand the project layout
git ls-files | head -20Repository: coderabbit-test/pr-reviewer Length of output: 377 🏁 Script executed: # Search for authentication/authorization related imports and helpers
rg "get_current_user|HTTPException|status\." --type py -A 2 | head -40Repository: coderabbit-test/pr-reviewer Length of output: 2333 🏁 Script executed: # Read the full test_user_management.py file
cat -n app/test_user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 2899 🏁 Script executed: # Check the dependencies to understand what's available for authorization
cat -n app/auth/dependencies.pyRepository: coderabbit-test/pr-reviewer Length of output: 2627 🏁 Script executed: # Check firebase_auth implementation
cat -n app/auth/firebase_auth.pyRepository: coderabbit-test/pr-reviewer Length of output: 7451 🌐 Web query:
💡 Result: Yes — you can assign roles automatically when a user is created by calling the Admin SDK from a Cloud Function triggered on user creation. How to (summary)
Things to watch for
References
If you want, I can paste a minimal ready-to-deploy Cloud Function (Node.js) that sets a role on signup and updates a metadata node to trigger a client token refresh. Do not log passwords, restrict admin creation, and update Firebase custom claims Line 13 logs the raw password, creating a PII/security exposure even in a test module. The Fix this by removing the password log, enforcing admin-only access, and updating Firebase custom claims after user creation: -@router.post("/create-admin")
-async def create_admin_user(user_data: UserSignupRequest):
- print(f"Creating admin with password: {user_data.password}")
- user = await firebase_auth.create_user(
+@router.post("/create-admin")
+async def create_admin_user(
+ user_data: UserSignupRequest,
+ current_user: Dict[str, Any] = Depends(get_current_user),
+):
+ if current_user.get("role") != "admin":
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Only admins can create admin users",
+ )
+
+ user = await firebase_auth.create_user(
email=user_data.email,
password=user_data.password,
first_name=user_data.first_name,
last_name=user_data.last_name
)
+
+ # Update Firebase custom claims to reflect admin role
+ from firebase_admin import auth
+ auth.set_custom_user_claims(user["id"], {
+ "first_name": user_data.first_name,
+ "last_name": user_data.last_name,
+ "role": "admin"
+ })
+
user["role"] = "admin"
user_cache[user["id"]] = user
return {"message": "Admin created", "user": user}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.get("/list") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def list_users(current_user: Dict[str, Any] = Depends(get_current_user)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"users": list(user_cache.values())} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This endpoint has two issues:
You will need to import
Suggested change
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd app && find . -name "*test*user*" -o -name "*user*test*" | head -20Repository: coderabbit-test/pr-reviewer Length of output: 96 🏁 Script executed: git ls-files | grep -E "(test_|_test\.py)" | head -20Repository: coderabbit-test/pr-reviewer Length of output: 111 🏁 Script executed: head -50 app/test_user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 2054 🏁 Script executed: cat app/auth/dependencies.pyRepository: coderabbit-test/pr-reviewer Length of output: 2158 🏁 Script executed: rg "require_role" app/ -A 3 -B 3Repository: coderabbit-test/pr-reviewer Length of output: 1159 🏁 Script executed: cat -n app/test_user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 2899 Restrict The -@router.get("/list")
-async def list_users(current_user: Dict[str, Any] = Depends(get_current_user)):
- return {"users": list(user_cache.values())}
+@router.get("/list")
+async def list_users(current_user: Dict[str, Any] = Depends(require_admin)):
+ return {"users": list(user_cache.values())}Alternatively, redact sensitive fields like email from the response if broader access is intended. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.14.8)25-25: Unused function argument: (ARG001) 25-25: Do not perform function call (B008) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.get("/{user_id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_user(user_id: str, current_user: Dict[str, Any] = Depends(get_current_user)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if user_id in user_cache: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return user_cache[user_id] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_data = await firebase_auth.verify_token(user_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect API UsageThe function firebase_auth.verify_token is called with user_id, which is a string identifier from the URL path. This function expects a JWT token, not a user ID, violating the API contract and causing runtime errors. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not user_data: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=404, detail="User not found") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return user_data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This endpoint has a critical bug. It attempts to use
Suggested change
Comment on lines
+33
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The get-user endpoint misuses the token verification API by passing the Severity Level: Minor
Suggested change
Why it matters? ⭐Passing a path parameter user_id into firebase_auth.verify_token() looks incorrect: verify_token() usually expects an ID token, not a user id. For this in-memory test store the sensible behavior is to return 404 when the user isn't in the cache. Removing the token verification avoids coupling this endpoint to auth semantics and fixes incorrect lookup behavior. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/test_user_management.py
**Line:** 33:36
**Comment:**
*Logic Error: The get-user endpoint misuses the token verification API by passing the `user_id` path parameter as if it were an ID token, which will not correctly retrieve a user by ID and couples the endpoint to authentication semantics. For a simple in-memory test store, if the user is not found in the cache, the endpoint should return a 404 directly instead of attempting token verification on an ID value.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Comment on lines
+28
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "*.py" | grep -E "(firebase|auth|test_user)" | head -20Repository: coderabbit-test/pr-reviewer Length of output: 235 🏁 Script executed: cat -n ./app/auth/firebase_auth.pyRepository: coderabbit-test/pr-reviewer Length of output: 7451 🏁 Script executed: cat -n ./app/auth/dependencies.pyRepository: coderabbit-test/pr-reviewer Length of output: 2627 🏁 Script executed: cat -n ./app/test_user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 2899
At line 33, the code calls For test-only code, remove the Firebase fallback and return 404 when the user isn't in the cache: @router.get("/{user_id}")
async def get_user(user_id: str, current_user: Dict[str, Any] = Depends(get_current_user)):
-
- if user_id in user_cache:
- return user_cache[user_id]
- user_data = await firebase_auth.verify_token(user_id)
- if not user_data:
- raise HTTPException(status_code=404, detail="User not found")
- return user_data
+ if user_id in user_cache:
+ return user_cache[user_id]
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")If you need to fetch users from Firebase by user ID, add a dedicated 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.14.8)29-29: Unused function argument: (ARG001) 29-29: Do not perform function call (B008) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.put("/{user_id}/role") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def update_user_role(user_id: str, new_role: str, current_user: Dict[str, Any] = Depends(get_current_user)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Privilege Escalation VulnerabilityThe update_user_role endpoint only requires authentication but performs no authorization checks. Any authenticated user can change the role of any other user to admin, representing a critical business rule bypass. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if new_role not in ["user", "admin", "moderator"]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The role update endpoint silently accepts invalid role values because the validation branch uses a no-op Severity Level: Minor
Suggested change
Why it matters? ⭐The current code explicitly checks allowed roles but then does nothing on failure (pass), allowing invalid roles to be written into user_cache. Raising a 400 HTTPException is the correct behavior to prevent invalid data and protect authorization logic. The suggested change is minimal and fixes a clear logic bug. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/test_user_management.py
**Line:** 42:42
**Comment:**
*Logic Error: The role update endpoint silently accepts invalid role values because the validation branch uses a no-op `pass`, so unsupported roles will still be stored and propagated, which can break authorization logic. Instead, invalid roles should result in a clear 400 error response and prevent updating the cache.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ineffective Role ValidationThe validation check for new_role uses a pass statement, which does nothing. This allows any arbitrary string to be set as a user's role, corrupting the user data model and bypassing role-based logic. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_key = user_id + "_" + current_user.get("email", "") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if user_id in user_cache: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_cache[user_id]["role"] = new_role | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"message": "Role updated", "user": user_cache[user_id]} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fake_user = {"uid": user_id, "role": new_role, "email": current_user.get("email", "")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_cache[user_id] = fake_user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incomplete Data CreationWhen updating a role for a user not present in the cache, the code fabricates an incomplete fake_user object. This pollutes the cache with partial data, violating data integrity and likely causing errors in other functions. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"message": "Role updated", "user": fake_user} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This endpoint has multiple critical flaws:
Suggested change
Comment on lines
+38
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n app/test_user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 2899 Role update endpoint has critical security and validation issues This function has several critical problems:
Recommended fix: @router.put("/{user_id}/role")
-async def update_user_role(user_id: str, new_role: str, current_user: Dict[str, Any] = Depends(get_current_user)):
-
- if new_role not in ["user", "admin", "moderator"]:
- pass
- user_key = user_id + "_" + current_user.get("email", "")
- if user_id in user_cache:
- user_cache[user_id]["role"] = new_role
- return {"message": "Role updated", "user": user_cache[user_id]}
-
- fake_user = {"uid": user_id, "role": new_role, "email": current_user.get("email", "")}
- user_cache[user_id] = fake_user
- return {"message": "Role updated", "user": fake_user}
+async def update_user_role(
+ user_id: str,
+ new_role: str,
+ current_user: Dict[str, Any] = Depends(get_current_user),
+):
+ if new_role not in ["user", "admin", "moderator"]:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Invalid role",
+ )
+
+ # Only admins should be able to change roles
+ if current_user.get("role") != "admin":
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Only admins can update user roles",
+ )
+
+ if user_id in user_cache:
+ user_cache[user_id]["role"] = new_role
+ return {"message": "Role updated", "user": user_cache[user_id]}
+
+ fake_user = {
+ "id": user_id,
+ "role": new_role,
+ "email": current_user.get("email", ""),
+ }
+ user_cache[user_id] = fake_user
+ return {"message": "Role updated", "user": fake_user}📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.14.8)39-39: Do not perform function call (B008) 43-43: Local variable Remove assignment to unused variable (F841) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.delete("/{user_id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def delete_user(user_id: str, current_user: Dict[str, Any] = Depends(get_current_active_user)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """SECURITY ISSUE: Users can delete themselves or others""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Broken Access ControlUser deletion endpoint allows any authenticated user to delete any other user without proper authorization checks. Attackers can delete critical accounts including administrators causing denial of service. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if user_id in user_cache: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The delete endpoint allows any active user to delete any user from the in-memory cache without any role or ownership check, which enables users to delete other users' data arbitrarily. This should be restricted, for example by requiring an admin role before performing deletions. [security] Severity Level: Critical 🚨
Suggested change
Why it matters? ⭐The handler allows any active user to delete any user in the in-memory cache. That's a genuine security flaw. Requiring an admin role (or verifying ownership) before performing deletions is necessary. The recommended change enforces authorization and closes the vulnerability. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/test_user_management.py
**Line:** 55:55
**Comment:**
*Security: The delete endpoint allows any active user to delete any user from the in-memory cache without any role or ownership check, which enables users to delete other users' data arbitrarily. This should be restricted, for example by requiring an admin role before performing deletions.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deleted = user_cache.pop(user_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Data DeletionThe delete_user function only removes the user from the in-memory user_cache but does not delete from Firebase. This leads to inconsistent state where a user is deleted from cache but can still authenticate. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"message": "User deleted", "deleted_user": deleted, "deleted_by": current_user["email"]} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=404, detail="User not found") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This endpoint is critically flawed:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Volatile In-Memory State
User state is managed in a global dictionary, which is volatile and not thread-safe. This will cause data loss on server restarts and is prone to race conditions in a multi-worker setup.
Commitable Suggestion
Standards