-
Notifications
You must be signed in to change notification settings - Fork 4
testing changes for codebase context #48
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,79 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from fastapi import APIRouter, Depends, HTTPException, status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .firebase_auth import firebase_auth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .dependencies import get_current_user, require_admin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Dict, Any, List | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from pydantic import BaseModel, EmailStr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from firebase_admin import auth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router = APIRouter(prefix="/users", tags=["user-management"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class UserUpdateRequest(BaseModel): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: EmailStr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_name: str | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_name: str | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class BulkUserRequest(BaseModel): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_ids: List[str] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: str | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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
Suggested change
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 Action ValidationThe Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.get("/profile") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_user_profile(current_user: Dict[str, Any] = Depends(get_current_user)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get detailed user profile information""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_record = auth.get_user(current_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. The
Suggested change
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. Blocking I/O in Async EndpointsSynchronous functions from the Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| custom_claims = auth.get_custom_user_claims(user_record.uid) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": user_record.uid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "email": user_record.email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "first_name": custom_claims["first_name"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "last_name": custom_claims["last_name"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+31
to
+32
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. Accessing
Suggested change
Comment on lines
+27
to
+32
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: Directly indexing Severity Level: Minor
Suggested change
Why it matters? ⭐This is a legitimate runtime risk: auth.get_custom_user_claims(...) can return None or a dict missing keys, and indexing directly will raise TypeError/KeyError. The improved code (coalesce to {} and use .get with defaults) prevents these exceptions and returns sensible empty strings instead of failing the whole request. This addresses a real potential bug with minimal, safe change. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/auth/user_management.py
**Line:** 27:32
**Comment:**
*Null Pointer: Directly indexing `custom_claims["first_name"]` and `custom_claims["last_name"]` without handling the case where `get_custom_user_claims` returns `None` or missing keys can lead to TypeError or KeyError when a user has no custom claims set.
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
+31
to
+32
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. Unsafe Custom Claim AccessDirect dictionary access on Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "is_active": not user_record.disabled, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "created_at": str(user_record.user_metadata.creation_timestamp) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+36
to
+37
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. Catching a generic
Comment on lines
+22
to
+37
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 read the file to verify the code at lines 22-37
find . -name "user_management.py" -type f | head -20Repository: coderabbit-test/pr-reviewer Length of output: 100 🏁 Script executed: # Read the entire file to understand context and verify lines 22-37
cat -n ./app/auth/user_management.pyRepository: coderabbit-test/pr-reviewer Length of output: 3575 🏁 Script executed: # Find and read the dependencies file
find . -name "dependencies.py" -type f | grep -E "auth|dependencies"Repository: coderabbit-test/pr-reviewer Length of output: 97 🏁 Script executed: cat -n ./app/auth/dependencies.pyRepository: coderabbit-test/pr-reviewer Length of output: 2627 🏁 Script executed: # Find and read firebase_auth.py
find . -name "firebase_auth.py" -type fRepository: coderabbit-test/pr-reviewer Length of output: 98 🏁 Script executed: cat -n ./app/auth/firebase_auth.pyRepository: coderabbit-test/pr-reviewer Length of output: 7451 🏁 Script executed: # Search for where "id" key might be set in current_user or user_data
rg -n "current_user\[.id.\]|\"id\":" ./app/auth/ -A 2 -B 2Repository: coderabbit-test/pr-reviewer Length of output: 2074 Fix critical bug: use The endpoint has three issues:
Use this fix: - user_record = auth.get_user(current_user["id"])
+ user_record = auth.get_user(current_user["uid"])
custom_claims = auth.get_custom_user_claims(user_record.uid) or {}
return {
"id": user_record.uid,
"email": user_record.email,
- "first_name": custom_claims["first_name"],
- "last_name": custom_claims["last_name"],
+ "first_name": custom_claims.get("first_name"),
+ "last_name": custom_claims.get("last_name"),
"is_active": not user_record.disabled,
"created_at": str(user_record.user_metadata.creation_timestamp)
}
except Exception as e:
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail="Failed to fetch user profile",
+ ) from eAlso apply the same 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.14.8)23-23: Do not perform function call (B008) 36-36: Do not catch blind exception: (BLE001) 37-37: Within an (B904) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.put("/profile") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def update_user_profile( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_data: UserUpdateRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_user: Dict[str, Any] = Depends(get_current_user) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Update current user's profile information""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_record = auth.get_user(current_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. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if update_data.email != user_record.email: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth.update_user(user_record.uid, email=update_data.email) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth.set_custom_user_claims(user_record.uid, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "first_name": update_data.first_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "last_name": update_data.last_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "role": current_user.get("role", "user") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"message": "Profile updated successfully"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+57
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. Catching a generic |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @router.post("/bulk-action") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def bulk_user_action( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request: BulkUserRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_user: Dict[str, Any] = Depends(get_current_user) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Perform bulk actions on multiple users (admin only)""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if current_user.get("role") != "admin": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+66
to
+67
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. Instead of manually checking for the 'admin' role, you should use the To fix this, you would:
Comment on lines
+66
to
+67
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 Admin AuthorizationManual role checking bypasses centralized authorization controls and creates inconsistent security patterns. The require_admin dependency is imported but not used, leading to potential authorization bypass if role validation logic differs. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| results = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 bulk user action endpoint silently accepts any Severity Level: Minor
Suggested change
Why it matters? ⭐Currently if request.action is not "disable" or "delete" the endpoint simply returns an empty results list and 200 OK, which is misleading and lets invalid input appear to succeed. Validating the action and returning a 400 for unsupported actions is correct behavior and prevents silent no-ops. This change fixes an input validation bug and improves API correctness. Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/auth/user_management.py
**Line:** 68:68
**Comment:**
*Logic Error: The bulk user action endpoint silently accepts any `action` value and returns a 200 response with an empty `results` list when the action is not `"disable"` or `"delete"`, which is misleading and makes invalid input appear successful.
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. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for user_id in request.user_ids: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. For the 'delete' action, the current implementation iterates through |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if request.action == "disable": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth.update_user(user_id, disabled=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| results.append({"user_id": user_id, "status": "disabled"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif request.action == "delete": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth.delete_user(user_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| results.append({"user_id": user_id, "status": "deleted"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+76
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 current logic doesn't handle cases where
Suggested change
Comment on lines
+74
to
+76
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. Inefficient Bulk DeletionThe code iterates through user IDs to delete them one by one, causing an N+1 query problem where N is the number of users. This results in N separate network requests, which is inefficient and scales poorly. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| results.append({"user_id": user_id, "status": "error", "message": str(e)}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+77
to
+78
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. While it's good that the exception is caught inside the loop to allow other operations to continue, catching a generic |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {"results": results} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |||||||||||||
| from fastapi.responses import JSONResponse | ||||||||||||||
| from .auth.routes import router as auth_router | ||||||||||||||
| from .example_protected_routes import router as protected_router | ||||||||||||||
| from .auth.user_management import router as user_management_router | ||||||||||||||
|
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 new import for the user management router will raise an ImportError at application startup because the Severity Level: Critical 🚨
Suggested change
Why it matters? ⭐The suggestion correctly identifies a real startup failure: the PR added "from .auth.user_management import router as user_management_router" Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** app/main.py
**Line:** 6:6
**Comment:**
*Possible Bug: The new import for the user management router will raise an ImportError at application startup because the `.auth.user_management` module does not exist in the codebase, causing the whole FastAPI app to fail to load; wrapping this import in a try/except with a safe fallback router prevents the crash while still using the router if/when the module is added.
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. |
||||||||||||||
| import os | ||||||||||||||
|
|
||||||||||||||
| # Create FastAPI app | ||||||||||||||
|
|
@@ -27,6 +28,9 @@ | |||||||||||||
| # Include protected routes (examples) | ||||||||||||||
| app.include_router(protected_router) | ||||||||||||||
|
|
||||||||||||||
| # Include user management routes | ||||||||||||||
| app.include_router(user_management_router) | ||||||||||||||
|
|
||||||||||||||
| # Global exception handler | ||||||||||||||
| @app.exception_handler(Exception) | ||||||||||||||
| async def global_exception_handler(request, exc): | ||||||||||||||
|
|
||||||||||||||
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.
🧩 Analysis chain
🏁 Script executed:
Repository: coderabbit-test/pr-reviewer
Length of output: 101
🏁 Script executed:
cat -n app/auth/user_management.py | head -100Repository: coderabbit-test/pr-reviewer
Length of output: 3575
Add Literal type constraint and explicit fallback for bulk operation actions
BulkUserRequest.actionis currently a plainstr, butbulk_user_actiononly handles"disable"and"delete". Any other value silently produces an empty or partialresultsarray, which masks client bugs.Strengthen the type safety and add explicit handling by:
Literalfromtyping(line 4)BulkUserRequest.actiontoLiteral["disable", "delete"](line 19)elseclause in the action handling loop to return a "skipped" status with an explanatory message for any unexpected action valuesAlso, switch the admin check from manual role verification (
if current_user.get("role") != "admin") to userequire_admindependency for consistency with the rest of the auth layer (line 63).🤖 Prompt for AI Agents