-
Notifications
You must be signed in to change notification settings - Fork 1
fix: production deployment blockers and security gaps #23
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
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 | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -105,12 +105,15 @@ | |||||||||||||||||||||||||||||||
| publish_limiter = None | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||
| from middleware.clerk_auth import get_current_user | ||||||||||||||||||||||||||||||||
| from middleware.clerk_auth import get_current_user, require_auth | ||||||||||||||||||||||||||||||||
| except ImportError: | ||||||||||||||||||||||||||||||||
| logger.error("clerk_auth_import_failed", detail="Authentication middleware unavailable - all authenticated endpoints will reject requests") | ||||||||||||||||||||||||||||||||
| async def get_current_user(): | ||||||||||||||||||||||||||||||||
| """Fallback that rejects all requests when auth middleware is unavailable.""" | ||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=503, detail="Authentication service unavailable") | ||||||||||||||||||||||||||||||||
| async def require_auth(): | ||||||||||||||||||||||||||||||||
| """Fallback that rejects all requests when auth middleware is unavailable.""" | ||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=503, detail="Authentication service unavailable") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| from services.db import get_database | ||||||||||||||||||||||||||||||||
| from repositories.posts import PostRepository | ||||||||||||||||||||||||||||||||
|
|
@@ -157,11 +160,11 @@ class BatchGenerateRequest(BaseModel): | |||||||||||||||||||||||||||||||
| @router.post("/generate-preview") | ||||||||||||||||||||||||||||||||
| async def generate_preview( | ||||||||||||||||||||||||||||||||
| req: GenerateRequest, | ||||||||||||||||||||||||||||||||
| current_user: dict = Depends(get_current_user) | ||||||||||||||||||||||||||||||||
| current_user: dict = Depends(require_auth) | ||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||
| """Generate an AI post preview from context. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Rate limited to 10 requests per hour per user to prevent abuse. | ||||||||||||||||||||||||||||||||
| Requires authentication. Rate limited to 10 requests per hour per user to prevent abuse. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Supports multiple AI providers with tier enforcement: | ||||||||||||||||||||||||||||||||
| - Free tier: Always routes to Groq (fast, free) | ||||||||||||||||||||||||||||||||
|
|
@@ -172,12 +175,8 @@ async def generate_preview( | |||||||||||||||||||||||||||||||
| if not generate_post_with_ai: | ||||||||||||||||||||||||||||||||
| raise HTTPException(status_code=503, detail="AI service not available") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Use authenticated user_id if available, otherwise fall back to request body | ||||||||||||||||||||||||||||||||
| user_id = None | ||||||||||||||||||||||||||||||||
| if current_user and current_user.get("user_id"): | ||||||||||||||||||||||||||||||||
| user_id = current_user["user_id"] | ||||||||||||||||||||||||||||||||
| elif req.user_id: | ||||||||||||||||||||||||||||||||
| user_id = req.user_id | ||||||||||||||||||||||||||||||||
| # Use the authenticated user's ID only — req.user_id is ignored to prevent impersonation | ||||||||||||||||||||||||||||||||
| user_id = current_user["user_id"] | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| user_id = current_user["user_id"] | |
| user_id = current_user["user_id"] | |
| # If the request body includes a user_id, ensure it matches the authenticated user | |
| request_user_id = getattr(req, "user_id", None) | |
| if request_user_id is not None and request_user_id != user_id: | |
| logger.warning( | |
| "user_id_mismatch_in_generate_preview_request", | |
| authenticated_user_id=(user_id[:8] if isinstance(user_id, str) else user_id), | |
| request_user_id=(request_user_id[:8] if isinstance(request_user_id, str) else request_user_id), | |
| ) | |
| raise HTTPException( | |
| status_code=403, | |
| detail="user_id in request body does not match authenticated user", | |
| ) |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -70,7 +70,7 @@ | |||
| OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', '') | ||||
| ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY', '') | ||||
| MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY', '') | ||||
| GITHUB_USERNAME = os.getenv('GITHUB_USERNAME', 'cliff-de-tech') | ||||
| GITHUB_USERNAME = os.getenv('GITHUB_USERNAME', '') | ||||
|
||||
| GITHUB_USERNAME = os.getenv('GITHUB_USERNAME', '') |
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.
This endpoint now ignores
req.user_idto prevent impersonation, but the request schema (GenerateRequest) still acceptsuser_idand tests/clients may continue sending it. Silent ignoring can mask client bugs and makes the API contract ambiguous. Consider removinguser_idfromGenerateRequestfor this endpoint, or explicitly rejecting requests that includeuser_id(or that include a value different from the authenticated user) with a 400/403.