Skip to content

Include private user profiles in search results#3034

Merged
awindsr merged 3 commits into
productionfrom
dev-server
Jul 19, 2026
Merged

Include private user profiles in search results#3034
awindsr merged 3 commits into
productionfrom
dev-server

Conversation

@DevWithPranav

Copy link
Copy Markdown
Collaborator

No description provided.

fix(user-search): include private user profiles in search results
Include private user profiles in user search results
@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes the user_settings_user__is_public=True filter from UserSearchAPI, which is a public endpoint with no authentication requirement. The stated intent is to allow private user profiles to appear in search results.

  • The removed filter was the only mechanism preventing private users (those with is_public = false, the default per the DB schema) from being surfaced at /api/v1/dashboard/user/search/ without any JWT or role check — exposing their name, muid, profile picture, karma, organizations, and interest groups to unauthenticated callers.
  • Every parallel privacy boundary in the codebase (profile_view.py across five sub-routes, mulearner_views.py, analytics_views.py, external_api_views.py) enforces the same is_public invariant; this change breaks it for the search surface specifically.

Confidence Score: 1/5

This change breaks a user-facing privacy contract on a public, unauthenticated endpoint and should not be merged as-is.

The single removed line was the sole guard keeping private users out of a public search endpoint with no JWT requirement. The database defaults is_public to false, so the vast majority of registered users never opted into public discoverability. Merging would immediately expose names, muids, profile pictures, karma scores, organizations, and interest groups for every private account to any unauthenticated caller.

api/dashboard/user/dash_user_views.py — the entire change is in this one file, specifically the UserSearchAPI queryset construction.

Security Review

  • Unauthenticated exposure of private user data (api/dashboard/user/dash_user_views.py, UserSearchAPI.get): The is_public=True filter was the sole barrier keeping private profiles out of a public, JWT-free search endpoint. With it removed, any unauthenticated caller can enumerate every user's id, full_name, muid, profile_pic, karma, organizations, and interest_groups regardless of their privacy setting. The database schema sets is_public DEFAULT '0', so most users are private by default and never consented to public discoverability.

Important Files Changed

Filename Overview
api/dashboard/user/dash_user_views.py Removes the is_public filter from UserSearchAPI — a public, unauthenticated endpoint — exposing all private user profiles (name, muid, karma, orgs) to any caller.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Unauthenticated Caller
    participant S as UserSearchAPI (GET /search/)
    participant DB as Database

    Note over C,S: No authentication_classes declared - No JWT required

    C->>S: "GET /api/v1/dashboard/user/search/?search=..."
    S->>DB: User.objects.all() (no is_public filter)
    DB-->>S: All users including private profiles
    S-->>C: id, full_name, muid, profile_pic, karma, organizations

    Note over C,S: Before this PR
    C->>S: "GET /api/v1/dashboard/user/search/?search=..."
    S->>DB: "User.objects.filter(user_settings_user__is_public=True)"
    DB-->>S: Only public profiles
    S-->>C: Public user data only
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Unauthenticated Caller
    participant S as UserSearchAPI (GET /search/)
    participant DB as Database

    Note over C,S: No authentication_classes declared - No JWT required

    C->>S: "GET /api/v1/dashboard/user/search/?search=..."
    S->>DB: User.objects.all() (no is_public filter)
    DB-->>S: All users including private profiles
    S-->>C: id, full_name, muid, profile_pic, karma, organizations

    Note over C,S: Before this PR
    C->>S: "GET /api/v1/dashboard/user/search/?search=..."
    S->>DB: "User.objects.filter(user_settings_user__is_public=True)"
    DB-->>S: Only public profiles
    S-->>C: Public user data only
Loading

Reviews (1): Last reviewed commit: "Merge pull request #3033 from gtech-mule..." | Re-trigger Greptile

Comment on lines 628 to 633
queryset = (
User.objects.all()
.select_related("wallet_user")
.filter(user_settings_user__is_public=True)
.prefetch_related("user_settings_user")
.order_by("-wallet_user__karma")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 security Private profiles exposed on an unauthenticated public endpoint

UserSearchAPI has no authentication_classes = [CustomizePermission] declaration, making it accessible without any JWT. The removed filter was the only guard preventing private profiles from appearing in results. user_settings.is_public defaults to 0 (false) in the schema, so the majority of users who never explicitly opted in are now discoverable — including their id, full_name, muid, profile_pic, karma, organizations, and interest_groups — by any unauthenticated caller.

Every other privacy boundary in the codebase enforces this invariant: profile_view.py gates five separate profile sub-endpoints on is_public, mulearner_views.py filters with user_settings_user__is_public=True, and external_api_views.py rejects private profiles with 403. Removing this single line breaks the contract users relied on when they left (or set) their visibility to private.

@awindsr
awindsr merged commit ca5a1c1 into production Jul 19, 2026
2 checks 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