Skip to content

Dev#33

Merged
ngovinh2k2 merged 20 commits into
mainfrom
dev
Jul 3, 2026
Merged

Dev#33
ngovinh2k2 merged 20 commits into
mainfrom
dev

Conversation

@ngovinh2k2

Copy link
Copy Markdown
Member

What?

Why?

How?

Testing?

  • Functional Testing
  • Security
  • Performance
  • Error Handling
  • Code Quality
  • Documentation
  • Database
  • Deployment
  • Final Review

Anything Else?

ngovinh2k2 added 20 commits June 8, 2026 17:02
…n-updating-or-deleting-image

fix: remove old image from s3 when updating or deleting image
…-pages-and-email

feat: implement api crud for auth pages and email
…d-and-theme-color

feat: implement api crud for brand and theme color
…template-folder

feat: update the email ui in the template folder
…pi-performance-monitoring-and-profiling

feat: implement django silk for api performance monitoring and profiling
…k-org

fix: update response for api check org
@ngovinh2k2
ngovinh2k2 merged commit f958891 into main Jul 3, 2026
1 check passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces customization features for organizations by adding the custom_email, custom_page, and organization_setting apps, allowing custom templates, themes, and logos. It also integrates django-silk for profiling in development. The review feedback identifies critical issues, including a potential TypeError in the _get_instance helper when a default callable is missing, an Insecure Direct Object Reference (IDOR) vulnerability in the organization settings update view, a duplicate email_type field in OrganizationEmailSerializer, and a Django migration anti-pattern where a service is imported at the top level of a migration.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +111 to +118
def _get_instance(self, queryset, data, fallback_field, default=None):
data = data.copy()
object_id = data.pop("id", None)
fallback_value = data.get(fallback_field)
instance = queryset.filter(id=object_id).first() if object_id else None
if instance is None and fallback_value:
instance = queryset.filter(**{fallback_field: fallback_value}).first()
return instance or default(data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The _get_instance helper method defines default=None as a parameter. However, when it is called for custom_pages (line 160), no default argument is provided. If a matching custom page is not found in the database, default(data) will be evaluated, raising a TypeError: 'NoneType' object is not callable and crashing the request.

To fix this, check if default is callable before executing it, and return None otherwise.

Suggested change
def _get_instance(self, queryset, data, fallback_field, default=None):
data = data.copy()
object_id = data.pop("id", None)
fallback_value = data.get(fallback_field)
instance = queryset.filter(id=object_id).first() if object_id else None
if instance is None and fallback_value:
instance = queryset.filter(**{fallback_field: fallback_value}).first()
return instance or default(data)
def _get_instance(self, queryset, data, fallback_field, default=None):
data = data.copy()
object_id = data.pop("id", None)
fallback_value = data.get(fallback_field)
instance = queryset.filter(id=object_id).first() if object_id else None
if instance is None and fallback_value:
instance = queryset.filter(**{fallback_field: fallback_value}).first()
if instance is not None:
return instance
return default(data) if callable(default) else None

Comment on lines +23 to +29
def get_object(self):
organization = get_object_or_404(
Organization,
slug_name=self.request.headers.get("X-Organization"),
is_active=True,
)
return get_object_or_404(self.get_queryset(), organization=organization)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The get_object method retrieves the organization solely based on the X-Organization header without verifying if the requesting user (identified by the X-User-ID header) is actually associated with or has a role in that organization. This creates an Insecure Direct Object Reference (IDOR) vulnerability, allowing any authenticated user to modify another organization's settings by spoofing the header.

To secure this endpoint, ensure that the query filters the organization by the user's ID.

Suggested change
def get_object(self):
organization = get_object_or_404(
Organization,
slug_name=self.request.headers.get("X-Organization"),
is_active=True,
)
return get_object_or_404(self.get_queryset(), organization=organization)
def get_object(self):
user_id = self.request.headers.get("X-User-ID")
organization_slug = self.request.headers.get("X-Organization")
organization = get_object_or_404(
Organization,
slug_name=organization_slug,
organization_role__organization_role_user__root_user_id=user_id,
is_active=True,
)
return get_object_or_404(self.get_queryset(), organization=organization)

Comment on lines +22 to +23
"email_type",
"email_type",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The email_type field is duplicated in the Meta.fields list. Remove the duplicate entry to keep the serializer definition clean.

Suggested change
"email_type",
"email_type",
"email_type",

@@ -0,0 +1,43 @@
from django.db import migrations

from apps.custom_email.service import get_default_organization_emails

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Importing from apps.custom_email.service at the top level of a migration is a Django anti-pattern. The service file imports live models (e.g., OrganizationEmail), which can cause migration failures in the future if those models are modified or deleted. Consider moving the default email data to a standalone constants file that does not import any models, or defining the default data directly within the migration.

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.

1 participant