Skip to content

Conversation

@rubybui
Copy link
Owner

@rubybui rubybui commented May 15, 2025

Description

Brief summary of the changes and any relevant context (design doc, screenshots of bugs...).

Fixes # (issue)

Changes

  • Feature: Describe new features or changes.
  • Bugfix: Outline any bug fixes.
  • Refactor: Note any code refactoring.

Testing

Describe tests run to verify changes. If manual testing has been done, provide instructions to reproduce.

  • Unit tests
  • Manual testing

Checklist:

  • Added tests for new features or bug fixes
  • (For the final review) I have refactored my code :)

Summary by CodeRabbit

  • New Features

    • Introduced REST API endpoints for managing emergency contacts, allowing users to create, view, update, and delete their emergency contacts.
    • Emergency contacts are now explicitly linked to user accounts, improving data association and security.
  • Bug Fixes

    • Improved permission checks and error handling for resource ownership and admin rights in emergency contact operations.
  • Chores

    • Updated environment variable management for development and production services to support loading from a .env file.

@coderabbitai
Copy link

coderabbitai bot commented May 15, 2025

Walkthrough

The changes introduce a complete emergency contacts feature to the application. This includes database schema updates, new API endpoints, service and repository logic, and model relationships. Supporting improvements were made to authentication, authorization, and error handling, as well as Docker configuration adjustments to load environment variables from a .env file.

Changes

File(s) Change Summary
docker-compose.yml Added env_file: .env to three service definitions to load environment variables from the .env file.
migrations/versions/596e8c1b8988_.py Migration script adds a non-nullable user_id column with a foreign key to users in emergency_contacts. Provides upgrade and downgrade logic.
mind_matter_api/api/__init__.py Integrated emergency contacts feature by initializing its service and registering its routes in the application.
mind_matter_api/api/emergency_contacts.py New module defining REST API endpoints for CRUD operations on emergency contacts, with authentication and ownership checks.
mind_matter_api/models/emergency_contacts.py Added user_id foreign key and user relationship to EmergencyContact model for linking contacts to users.
mind_matter_api/models/surveys.py Added user relationship to Survey model for ORM-level user association.
mind_matter_api/models/users.py Added surveys and emergency_contacts relationships to User model; changed JWT secret key access in decode_auth_token.
mind_matter_api/repositories/emergency_contacts.py Refactored repository to generic base, added get_by_user_id method, and updated constructor.
mind_matter_api/services/emergency_contacts.py Refactored service for granular contact management, updated method signatures, added type hints, and improved logging.
mind_matter_api/utils/auth.py Enhanced admin and ownership checks, removed some debug logs, added repository-based user retrieval, and improved error logging.
mind_matter_api/utils/decorators.py Improved require_owner decorator with error handling, logging, and permission denial logging.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant API (Flask)
    participant EmergencyContactsService
    participant EmergencyContactRepository
    participant Database

    User->>API (Flask): Authenticated request (GET /emergency-contacts)
    API (Flask)->>EmergencyContactsService: get_emergency_contacts(user_id)
    EmergencyContactsService->>EmergencyContactRepository: get_by_user_id(user_id)
    EmergencyContactRepository->>Database: Query emergency_contacts by user_id
    Database-->>EmergencyContactRepository: List of contacts
    EmergencyContactRepository-->>EmergencyContactsService: List of contacts
    EmergencyContactsService-->>API (Flask): List of contacts
    API (Flask)-->>User: JSON response

    User->>API (Flask): Authenticated request (POST /emergency-contacts)
    API (Flask)->>EmergencyContactsService: create_emergency_contacts(user_id, contact_data)
    EmergencyContactsService->>EmergencyContactRepository: create(new_contact)
    EmergencyContactRepository->>Database: Insert new contact
    Database-->>EmergencyContactRepository: Created contact
    EmergencyContactRepository-->>EmergencyContactsService: Created contact
    EmergencyContactsService-->>API (Flask): Created contact
    API (Flask)-->>User: JSON response (201 Created)

    User->>API (Flask): Authenticated request (PUT /emergency-contacts/<id>)
    API (Flask)->>EmergencyContactsService: update_emergency_contacts(contact_id, contact_data)
    EmergencyContactsService->>EmergencyContactRepository: update(contact_id, contact_data)
    EmergencyContactRepository->>Database: Update contact
    Database-->>EmergencyContactRepository: Updated contact
    EmergencyContactRepository-->>EmergencyContactsService: Updated contact
    EmergencyContactsService-->>API (Flask): Updated contact
    API (Flask)-->>User: JSON response

    User->>API (Flask): Authenticated request (DELETE /emergency-contacts/<id>)
    API (Flask)->>EmergencyContactsService: delete_emergency_contacts(contact_id)
    EmergencyContactsService->>EmergencyContactRepository: delete(contact_id)
    EmergencyContactRepository->>Database: Delete contact
    Database-->>EmergencyContactRepository: Success
    EmergencyContactRepository-->>EmergencyContactsService: None
    EmergencyContactsService-->>API (Flask): None
    API (Flask)-->>User: 204 No Content
Loading

Poem

🐰
New contacts hop in, a field now grown,
With user links and seeds well sown.
Routes for help, both add and prune,
Ownership checked by the light of the moon.
The database garden, neat and bright—
Emergency friends are safe tonight!
🌱✨

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (2)
docker-compose.yml (2)

36-37: Ensure consistency of env_file usage for the prod service.
Same guidance applies here for mind-matter-flask-prod: confirm the existence of .env, its git-ignoring, and consider using a dedicated .env.production file.


52-53: Ensure consistency of env_file usage for the manage service.
Same guidance applies here for mind-matter-manage: verify .env presence and ignore settings (or use a context-specific env file).

🧹 Nitpick comments (10)
migrations/versions/596e8c1b8988_.py (1)

21-24: Consider named constraints for better maintainability

The foreign key constraint is created without a specific name (None), which might make future schema maintenance more difficult. Consider using explicit constraint names for easier tracking and maintenance.

- batch_op.create_foreign_key(None, 'users', ['user_id'], ['user_id'])
+ batch_op.create_foreign_key('fk_emergency_contact_user', 'users', ['user_id'], ['user_id'])
mind_matter_api/utils/decorators.py (2)

2-2: Remove unused request import

request isn’t referenced anywhere in the module; keeping it around will fail ruff (F401) and slightly increase import time.

-from flask import request, abort, current_app as app
+from flask import abort, current_app as app
🧰 Tools
🪛 Ruff (0.8.2)

2-2: flask.request imported but unused

Remove unused import: flask.request

(F401)


6-6: Avoid configuring global logging inside a library/helper module

Calling logging.basicConfig() at import time overrides the log format/level for the whole process, which is the application’s responsibility (often done in the entry-point or a dedicated logging config file).
Consider removing this line and letting the Flask app (or a CLI) decide the log configuration.

-logging.basicConfig(level=logging.DEBUG)
+# Prefer: configure logging in the main application initialisation code
mind_matter_api/models/emergency_contacts.py (1)

8-8: Consider adding ondelete="CASCADE" and an index on user_id

Most queries will fetch contacts by user_id; an index greatly improves performance.
ondelete="CASCADE" (paired with SQLAlchemy’s passive_deletes=True if needed) keeps orphaned contacts from accumulating when a user is removed.

-    user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'), nullable=False)
+    user_id = db.Column(
+        db.Integer,
+        db.ForeignKey('users.user_id', ondelete="CASCADE"),
+        nullable=False,
+        index=True,
+    )
mind_matter_api/repositories/emergency_contacts.py (2)

3-4: Remove unused db import

db isn’t referenced after the refactor; keeping it violates lint rules (F401).

-from mind_matter_api.models import db
🧰 Tools
🪛 Ruff (0.8.2)

4-4: mind_matter_api.models.db imported but unused

Remove unused import: mind_matter_api.models.db

(F401)


10-12: Type-safe filtering in get_by_user_id

user_id is annotated as str but the column is Integer; relying on implicit coercion can hurt performance and break on some DBs (e.g., PostgreSQL with strict casting).

-    def get_by_user_id(self, user_id: str) -> List[EmergencyContact]:
-        return self.session.query(EmergencyContact).filter(EmergencyContact.user_id == user_id).all()
+    def get_by_user_id(self, user_id: int) -> List[EmergencyContact]:
+        return (
+            self.session.query(EmergencyContact)
+            .filter(EmergencyContact.user_id == int(user_id))
+            .all()
+        )

Optionally, add pagination to keep large result sets manageable.

mind_matter_api/utils/auth.py (1)

31-35: Repository/Service instantiation on every call

Creating UserRepository() and UserService() per admin check adds connection overhead on hot paths (every request hitting an admin-guarded endpoint).

Consider:

  1. Re-using a single repository/service per request (store in g or a lightweight cache), or
  2. Moving the admin role check into a DB query that only fetches the role column.

This keeps latency low under load.

mind_matter_api/api/emergency_contacts.py (2)

9-10: Avoid calling logging.basicConfig in library / route modules

Invoking basicConfig in a non-entrypoint file can:

  1. Override logging configuration established by the application’s main entrypoint or tests.
  2. Add multiple handlers when this module is imported repeatedly (e.g., in unit tests with flask.testing.FlaskClient).

Instead, rely on the application-level configuration done in app.py/wsgi.py, or conditionally add a module-specific logger.


37-38: Avoid double (de)serialization round-trip

validated_data = schema.load(); schema.dump(validated_data) immediately serialises the object back into a dict, doing redundant work.

-validated_data = EmergencyContactSchema().load(request_data)
-contact_data = EmergencyContactSchema().dump(validated_data)
+contact_data = EmergencyContactSchema().load(request_data)
mind_matter_api/services/emergency_contacts.py (1)

7-8: Move logger configuration out of the service module

As with the API file, calling logging.basicConfig inside a library class can clash with the main application’s configuration and add duplicate handlers.

Remove these lines and rely on application-level setup.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 499f872 and a697449.

⛔ Files ignored due to path filters (1)
  • mind_matter_api/.DS_Store is excluded by !**/.DS_Store
📒 Files selected for processing (11)
  • docker-compose.yml (3 hunks)
  • migrations/versions/596e8c1b8988_.py (1 hunks)
  • mind_matter_api/api/__init__.py (2 hunks)
  • mind_matter_api/api/emergency_contacts.py (1 hunks)
  • mind_matter_api/models/emergency_contacts.py (1 hunks)
  • mind_matter_api/models/surveys.py (1 hunks)
  • mind_matter_api/models/users.py (2 hunks)
  • mind_matter_api/repositories/emergency_contacts.py (1 hunks)
  • mind_matter_api/services/emergency_contacts.py (1 hunks)
  • mind_matter_api/utils/auth.py (2 hunks)
  • mind_matter_api/utils/decorators.py (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
mind_matter_api/utils/decorators.py (1)
mind_matter_api/utils/auth.py (2)
  • is_user_admin (29-38)
  • is_user_owner (40-64)
mind_matter_api/api/emergency_contacts.py (5)
mind_matter_api/schemas.py (1)
  • EmergencyContactSchema (177-181)
mind_matter_api/services/emergency_contacts.py (5)
  • EmergencyContactsService (9-34)
  • get_emergency_contacts (13-15)
  • create_emergency_contacts (17-20)
  • update_emergency_contacts (22-31)
  • delete_emergency_contacts (33-34)
mind_matter_api/utils/decorators.py (1)
  • require_auth (8-13)
mind_matter_api/utils/auth.py (2)
  • is_user_owner (40-64)
  • is_user_admin (29-38)
mind_matter_api/repositories/emergency_contacts.py (1)
  • get_by_id (18-19)
🪛 Ruff (0.8.2)
mind_matter_api/repositories/emergency_contacts.py

4-4: mind_matter_api.models.db imported but unused

Remove unused import: mind_matter_api.models.db

(F401)

mind_matter_api/utils/decorators.py

2-2: flask.request imported but unused

Remove unused import: flask.request

(F401)

mind_matter_api/api/emergency_contacts.py

1-1: flask.current_app imported but unused

Remove unused import: flask.current_app

(F401)


5-5: mind_matter_api.utils.decorators.require_owner imported but unused

Remove unused import: mind_matter_api.utils.decorators.require_owner

(F401)

🔇 Additional comments (8)
mind_matter_api/models/surveys.py (1)

15-15: Excellent addition of the bidirectional relationship to User model

This relationship addition creates a proper bidirectional link with the User model, which aligns with SQLAlchemy best practices and complements the existing foreign key. This enables cleaner ORM-level navigation between Survey and User instances.

mind_matter_api/api/__init__.py (2)

18-21: Well-structured imports for the emergency contacts feature

The imports follow the established pattern in the codebase with clear organization and descriptive comments.


37-39: Proper integration of emergency contacts service and routes

The emergency contacts feature initialization follows the same architectural pattern used for other services in the application. Good job maintaining consistency in the application structure.

mind_matter_api/models/users.py (2)

36-37: Good implementation of bidirectional relationships

These relationships follow SQLAlchemy best practices and properly complement the relationships defined in the Survey and EmergencyContact models, enabling clean ORM-level navigation between related entities.


71-71: Improved consistency in secret key access

This change aligns with how the secret key is accessed in the encode_auth_token method (line 54), creating better consistency in the authentication implementation.

migrations/versions/596e8c1b8988_.py (1)

21-24:

Details

❓ Verification inconclusive

Verify handling of existing emergency contact records

The migration adds a non-nullable user_id column without a default value. This could cause the migration to fail if there are existing emergency contact records in the database.


🏁 Script executed:

#!/bin/bash
# Check if there are existing records in the emergency_contacts table
# that would need user_id assignments during migration

# Use SQLite or PostgreSQL command based on your database
echo "Checking for existing emergency contacts..."
sqlite3 instance/test.db "SELECT COUNT(*) FROM emergency_contacts;" 2>/dev/null || \
psql -U postgres -d mind_matter -c "SELECT COUNT(*) FROM emergency_contacts;" 2>/dev/null

# Also check the schema to confirm understanding
echo "Current schema of emergency_contacts table:"
sqlite3 instance/test.db ".schema emergency_contacts" 2>/dev/null || \
psql -U postgres -d mind_matter -c "\d emergency_contacts" 2>/dev/null

Length of output: 460


Verify existing emergency_contacts before adding NOT NULL user_id

This migration in migrations/versions/596e8c1b8988_.py (lines 21–24) adds a non-nullable user_id column without a default. If any emergency_contacts rows already exist, the ALTER will fail.

Points to address:

  • Confirm that emergency_contacts is empty prior to running this migration, OR
  • Change the new column to nullable, backfill user_id values, then alter it to NOT NULL, OR
  • Add a server_default for user_id (and remove it after backfill)

Please verify your existing data handling or adjust the migration accordingly.

mind_matter_api/models/emergency_contacts.py (1)

14-15: Verify reciprocal relationship on User model

back_populates="emergency_contacts" assumes User.emergency_contacts is defined.
Please double-check that side exists; otherwise SQLAlchemy will raise at startup.
If missing, add:

emergency_contacts = db.relationship(
    "EmergencyContact",
    back_populates="user",
    cascade="all, delete-orphan",
)
mind_matter_api/utils/auth.py (1)

51-60: Edge cases in integer conversion

int(user_id) will raise if user_id is something unexpected (e.g., UUID).
If the application plans to migrate to UUIDs later, guard the conversion or compare as strings.

-            user_id_int = int(user_id) if isinstance(user_id, str) else user_id
+            try:
+                user_id_int = int(user_id)
+            except (TypeError, ValueError):
+                user_id_int = user_id  # fall back to original value

Comment on lines +19 to +20
env_file:
- .env
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add env_file to load environment variables for the dev service.
This directive ensures that variables declared in .env are injected into the mind-matter-flask-dev container. Please verify that:

  1. A .env file exists at the repository root.
  2. .env is listed in .gitignore (or equivalent) to avoid committing secrets.

Additionally, consider splitting environment files by context (e.g., .env.development, .env.production) and referencing them here to clearly separate dev vs. prod variables.


🏁 Script executed:

#!/bin/bash
# Verify .env file presence and git-ignoring
if [ ! -f .env ]; then
  echo "ERROR: .env file is missing in the repo root"
  exit 1
fi
if ! grep -q '^\.env$' .gitignore; then
  echo "ERROR: .env is not listed in .gitignore"
  exit 1
fi
echo "OK: .env exists and is git-ignored"

Length of output: 129


Add a .env file for the env_file directive
The env_file: setting in docker-compose.yml (lines 19–20) will fail because no .env file exists at the repo root. Please:

  • Create a root-level .env file with your development variables.
  • Add .env to .gitignore to prevent committing secrets.
  • (Optional) Split configs—e.g. .env.development / .env.production—and update env_file: accordingly to keep environments separate.
🤖 Prompt for AI Agents
In docker-compose.yml at lines 19-20, the env_file directive references a .env
file that does not exist at the repository root. To fix this, create a .env file
at the root containing your development environment variables, ensure that .env
is added to .gitignore to prevent committing sensitive data, and optionally
consider splitting environment files by context (e.g., .env.development and
.env.production) and update the env_file path accordingly to separate
configurations for different environments.

Comment on lines +28 to +38
try:
resource = resource_getter(*args, **kwargs)

if not is_user_owner(user_id, resource) and not is_user_admin(user_id):
app.logger.debug(f"[require_owner] Permission denied for user {user_id}")
abort(403, description="Permission denied")

return f(user_id, resource, *args, **kwargs)
except Exception as e:
app.logger.error(f"[require_owner] Error checking ownership: {str(e)}")
abort(403, description="Error checking permissions")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Over-broad except hides real errors and returns misleading 403

except Exception swallows programming/DB errors (e.g., Sequelize session issues) and replies with “Permission denied” (403).
This makes debugging very hard and leaks no useful information to clients.

Recommended approach:

  1. Narrow the try/except to only wrap the ownership checks.
  2. Log and re-raise unexpected errors as a 500 instead of 403.
-            try:
-                resource = resource_getter(*args, **kwargs)
-
-                if not is_user_owner(user_id, resource) and not is_user_admin(user_id):
-                    app.logger.debug(f"[require_owner] Permission denied for user {user_id}")
-                    abort(403, description="Permission denied")
-
-                return f(user_id, resource, *args, **kwargs)
-            except Exception as e:
-                app.logger.error(f"[require_owner] Error checking ownership: {str(e)}")
-                abort(403, description="Error checking permissions")
+            resource = None
+            try:
+                resource = resource_getter(*args, **kwargs)
+            except Exception as e:
+                app.logger.exception("[require_owner] Failed to load resource")
+                abort(500, description="Internal server error")
+
+            if not is_user_owner(user_id, resource) and not is_user_admin(user_id):
+                app.logger.debug(f"[require_owner] Permission denied for user {user_id}")
+                abort(403, description="Permission denied")
+
+            return f(user_id, resource, *args, **kwargs)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
resource = resource_getter(*args, **kwargs)
if not is_user_owner(user_id, resource) and not is_user_admin(user_id):
app.logger.debug(f"[require_owner] Permission denied for user {user_id}")
abort(403, description="Permission denied")
return f(user_id, resource, *args, **kwargs)
except Exception as e:
app.logger.error(f"[require_owner] Error checking ownership: {str(e)}")
abort(403, description="Error checking permissions")
resource = None
try:
resource = resource_getter(*args, **kwargs)
except Exception as e:
app.logger.exception("[require_owner] Failed to load resource")
abort(500, description="Internal server error")
if not is_user_owner(user_id, resource) and not is_user_admin(user_id):
app.logger.debug(f"[require_owner] Permission denied for user {user_id}")
abort(403, description="Permission denied")
return f(user_id, resource, *args, **kwargs)
🤖 Prompt for AI Agents
In mind_matter_api/utils/decorators.py around lines 28 to 38, the current
try/except block is too broad, catching all exceptions and returning a 403
error, which hides real errors and misleads clients. Refactor the code to wrap
only the ownership check calls (is_user_owner and is_user_admin) in the try
block. If an expected permission error occurs, abort with 403; for unexpected
exceptions, log the error and abort with a 500 status to indicate a server
error, allowing better debugging and clearer client feedback.

from marshmallow import ValidationError
from mind_matter_api.schemas import EmergencyContactSchema
from mind_matter_api.services.emergency_contacts import EmergencyContactsService
from mind_matter_api.utils.decorators import require_auth, require_owner
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Drop unused require_owner import

require_owner is imported but not used anywhere in the file. Eliminating it keeps the import list tidy and resolves the Ruff F401 warning.

-from mind_matter_api.utils.decorators import require_auth, require_owner
+from mind_matter_api.utils.decorators import require_auth
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from mind_matter_api.utils.decorators import require_auth, require_owner
-from mind_matter_api.utils.decorators import require_auth, require_owner
+from mind_matter_api.utils.decorators import require_auth
🧰 Tools
🪛 Ruff (0.8.2)

5-5: mind_matter_api.utils.decorators.require_owner imported but unused

Remove unused import: mind_matter_api.utils.decorators.require_owner

(F401)

🤖 Prompt for AI Agents
In mind_matter_api/api/emergency_contacts.py at line 5, the import statement
includes require_owner which is not used anywhere in the file. Remove
require_owner from the import statement to clean up unused imports and resolve
the Ruff F401 warning.

@@ -0,0 +1,98 @@
from flask import jsonify, request, current_app
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused current_app import to satisfy linter and avoid dead code

current_app is never referenced in this module. Ruff already flags this (F401), so the PR will fail the lint stage until it is removed.

-from flask import jsonify, request, current_app
+from flask import jsonify, request
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from flask import jsonify, request, current_app
-from flask import jsonify, request, current_app
+from flask import jsonify, request
🧰 Tools
🪛 Ruff (0.8.2)

1-1: flask.current_app imported but unused

Remove unused import: flask.current_app

(F401)

🤖 Prompt for AI Agents
In mind_matter_api/api/emergency_contacts.py at line 1, the import statement
includes `current_app` which is not used anywhere in the file. Remove
`current_app` from the import list to satisfy the linter and avoid dead code,
ensuring the PR passes the lint stage.

Comment on lines +22 to +23
app.logger.error(f"Error retrieving emergency contacts: {str(e)}")
return jsonify({'error': str(e)}), 500
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Return generic error message to avoid leaking internals

str(e) may expose stack-trace fragments or database information to clients. Return a generic message and log the full details server side.

-    return jsonify({'error': str(e)}), 500
+    app.logger.exception("Error retrieving emergency contacts")  # logs stack-trace
+    return jsonify({'error': 'Internal server error'}), 500
🤖 Prompt for AI Agents
In mind_matter_api/api/emergency_contacts.py around lines 22 to 23, the current
code returns the full error message string to the client, which may expose
sensitive internal details. Modify the return statement to send a generic error
message like "Internal server error" to the client, while keeping the detailed
error logged on the server side using app.logger.error.

Comment on lines +30 to +38
# Get and validate the request data
request_data = request.get_json()

# Add user_id to request data before validation
request_data['user_id'] = user_id

# Validate the complete data and convert to dict
validated_data = EmergencyContactSchema().load(request_data)
contact_data = EmergencyContactSchema().dump(validated_data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle missing / invalid JSON body early

request.get_json() can return None when the Content-Type header is wrong or the body is empty, causing a TypeError at request_data['user_id'].

-request_data = request.get_json()
+request_data = request.get_json(silent=True)
+if not isinstance(request_data, dict):
+    return jsonify({'error': 'JSON body required'}), 400

This small guard improves robustness and user feedback.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Get and validate the request data
request_data = request.get_json()
# Add user_id to request data before validation
request_data['user_id'] = user_id
# Validate the complete data and convert to dict
validated_data = EmergencyContactSchema().load(request_data)
contact_data = EmergencyContactSchema().dump(validated_data)
# Get and validate the request data
request_data = request.get_json(silent=True)
if not isinstance(request_data, dict):
return jsonify({'error': 'JSON body required'}), 400
# Add user_id to request data before validation
request_data['user_id'] = user_id
# Validate the complete data and convert to dict
validated_data = EmergencyContactSchema().load(request_data)
contact_data = EmergencyContactSchema().dump(validated_data)
🤖 Prompt for AI Agents
In mind_matter_api/api/emergency_contacts.py around lines 30 to 38, the code
assumes request.get_json() always returns a dictionary, but it can return None
if the JSON body is missing or invalid, causing a TypeError when accessing
request_data['user_id']. Add a check immediately after calling
request.get_json() to verify the result is not None; if it is, return an
appropriate error response indicating the JSON body is missing or invalid before
proceeding with adding user_id and validation.

Comment on lines +68 to +71
# Validate the data
data = EmergencyContactSchema().load(request_data, partial=True)
# Update the contact
updated = svc.update_emergency_contacts(contact_id, data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent privilege escalation by disallowing user_id updates

A caller could include "user_id": some_other_user in the JSON payload and transfer ownership. Strip the field before updating (or explicitly forbid it).

-data = EmergencyContactSchema().load(request_data, partial=True)
-if 'user_id' in data:
-    del data['user_id']
+data = EmergencyContactSchema(exclude=('user_id',)).load(request_data, partial=True)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mind_matter_api/api/emergency_contacts.py around lines 68 to 71, the update
logic allows the user_id field to be updated from the request data, which can
lead to privilege escalation by transferring ownership. To fix this, explicitly
remove or ignore the user_id field from the validated data before calling the
update_emergency_contacts method, ensuring that user_id cannot be changed
through the update payload.

Comment on lines +24 to +30
if isinstance(contact_data, EmergencyContact):
contact_data = {
'contact_name': contact_data.contact_name,
'phone_number': contact_data.phone_number,
'description': contact_data.description
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against unintended field updates in update_emergency_contacts

The current implementation forwards any keys in contact_data directly to the repository.
Add an allow-list or strip dangerous fields (id, user_id) to prevent ownership or PK tampering.

🤖 Prompt for AI Agents
In mind_matter_api/services/emergency_contacts.py around lines 24 to 30, the
code converts an EmergencyContact object to a dictionary but then forwards all
keys in contact_data directly to the repository, risking unintended updates to
protected fields like id or user_id. To fix this, add an allow-list of safe
fields (e.g., contact_name, phone_number, description) or explicitly remove id
and user_id from contact_data before passing it on, ensuring these critical
fields cannot be tampered with during updates.

Comment on lines +17 to +21
def create_emergency_contacts(self, user_id: str, contact_data: Dict[str, Any]) -> EmergencyContact:
new_contact = EmergencyContact(**contact_data)
created_contact = self.emergency_contacts_repository.create(new_contact)
return created_contact

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

user_id parameter is ignored – possible data integrity bug

create_emergency_contacts accepts user_id but never uses it, trusting the caller-supplied contact_data.
If a malicious client injects a different user_id, they can create contacts under another user’s account.

-def create_emergency_contacts(self, user_id: str, contact_data: Dict[str, Any]) -> EmergencyContact:
-    new_contact = EmergencyContact(**contact_data)
+def create_emergency_contacts(self, user_id: str, contact_data: Dict[str, Any]) -> EmergencyContact:
+    # Enforce correct ownership
+    contact_data['user_id'] = user_id
+    new_contact = EmergencyContact(**contact_data)
     created_contact = self.emergency_contacts_repository.create(new_contact)
     return created_contact
🤖 Prompt for AI Agents
In mind_matter_api/services/emergency_contacts.py around lines 17 to 21, the
user_id parameter is accepted but not used, which risks data integrity by
allowing contact_data to specify a different user_id. To fix this, explicitly
set the user_id field of the new EmergencyContact instance to the provided
user_id argument, overriding any user_id in contact_data before creating the
contact. This ensures the contact is always associated with the correct user.

@rubybui rubybui merged commit c72a5b6 into main May 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants