Skip to content

Conversation

@vaind
Copy link
Contributor

@vaind vaind commented Oct 23, 2025

Summary

Adds support for platform-restricted builtin symbol sources with organization-level access control.

This enables symbol sources to be restricted to specific platforms (e.g., console platforms like Nintendo Switch) and only accessible to organizations with the appropriate console platform access enabled.

Companion PR: getsentry/getsentry#18867 - Defines the Nintendo symbol source configuration (credentials, bucket settings, etc.)

Changes

Platform Filtering for Builtin Sources API

  • builtin_symbol_sources.py: Add platform-based filtering
    • Filter sources by platform query parameter
    • Check organization's enabledConsolePlatforms access for restricted sources
    • Backward compatible (works without platform parameter)

Default Symbol Sources Enhancement

  • default_symbol_sources.py: Enhanced helper for platform-specific defaults
    • Added nintendo-switch platform mapping to nintendo source
    • Check platform restrictions and org access before adding sources
    • Organization auto-fetched from project if not provided

Shared Utility

  • console_platforms.py: New utility module
    • organization_has_console_platform_access() - checks if org has access to a console platform
    • Used by both builtin sources endpoint and tempest utils

Schema Update

  • sources.py: Add platforms field to source schema

Refactoring

  • tempest/utils.py: Refactored to use shared organization_has_console_platform_access utility
  • team_projects.py: Pass organization to set_default_symbol_sources()

How It Works

Platform Restrictions

Sources can include a platforms key to restrict visibility:

"nintendo": {
    "type": "s3",
    "platforms": ["nintendo-switch"],  # Only visible to nintendo-switch projects
    # ... other config (defined in getsentry)
}

Two-layer filtering:

  1. Platform match: Request's platform must match source's platforms list
  2. Org access: Organization must have platform in enabledConsolePlatforms

Default Symbol Sources

DEFAULT_SYMBOL_SOURCES = {
    "electron": ["ios", "microsoft", "electron"],
    "unity": ["ios", "microsoft", "android", "nuget", "unity", "nvidia", "ubuntu"],
    "unreal": ["ios", "microsoft", "android", "nvidia", "ubuntu"],
    "godot": ["ios", "microsoft", "android", "nuget", "nvidia", "ubuntu"],
    "nintendo-switch": ["nintendo"],
}

Test Plan

  • Unit tests pass
  • Pre-commit hooks pass
  • Mypy type checking passes
  • Verify Nintendo Switch project in enabled org sees the source
  • Verify Nintendo Switch project in non-enabled org does NOT see the source
  • Verify non-Nintendo projects never see the source
  • Verify public sources still visible to all platforms
  • Verify API works without platform parameter (backward compatibility)

…org access control

Adds support for restricting builtin symbol sources to specific platforms with
organization-level access control via enabledConsolePlatforms.

## Changes

- Add `platforms` key to builtin source definitions in server.py to restrict
  sources to specific platforms (e.g., nintendo-switch)
- Update builtin_symbol_sources endpoint to filter sources based on:
  1. Platform match: request platform must match source's platforms list
  2. Org access: organization must have console platform enabled
- Pass project platform from frontend to API for filtering
- Add nintendo-private S3 source with credentials from environment variables
- Add platform mapping for nintendo-switch in DEFAULT_SYMBOL_SOURCES

## Implementation Details

Sources without a `platforms` key remain visible to all platforms (backward compatible).
Sources with `platforms` are only visible when:
- The request includes a matching platform parameter AND
- The organization has that console platform in enabledConsolePlatforms

This ensures platform-restricted sources (like Nintendo's private symbol server)
are only accessible to authorized organizations with matching project platforms.
@github-actions github-actions bot added Scope: Frontend Automatically applied to PRs that change frontend components Scope: Backend Automatically applied to PRs that change backend components labels Oct 23, 2025
@github-actions
Copy link
Contributor

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

@vaind vaind changed the title feat(symsorter): Add platform-restricted builtin symbol sources with org access control feat(symbols): Add platform-restricted builtin symbol sources with org access control Oct 23, 2025
@priscilawebdev
Copy link
Member

priscilawebdev commented Oct 28, 2025

Hmm, so as I understand it, we’re introducing a new private symbol source default nintendo-private that should be applied conditionally based on the platform.

Instead of adding the nintendo-private source to the defaults and then filtering it out later in the endpoint, shouldn't we only add it in the first place if the organization has nintendo-switch in their enabled consoles? This would probably live in the set_default_symbol_sources function.

The reason I’m thinking that way is: if we process a symbol source marked as a private default, we probably shouldn’t omit it later.

Also, just a quick note - I noticed this PR includes both frontend and backend changes. We usually try to keep those separate in different PRs 😉

vaind and others added 3 commits October 28, 2025 13:07
Co-authored-by: Priscila Oliveira <priscilawebdev@gmail.com>
…org access control

Adds support for platform-restricted symbol sources (e.g., nintendo-private) with
organization-level access control for console platforms.

Changes:
- Add 'platforms' field to COMMON_SOURCE_PROPERTIES schema for validation
- Add "nintendo-switch": ["nintendo-private"] to DEFAULT_SYMBOL_SOURCES mapping
- Update set_default_symbol_sources() to check organization access for platform-restricted sources
  - Checks source's 'platforms' field against org's 'enabled_console_platforms' option
  - Only adds source if org has access to at least one required platform
  - Supports both Django Project and RpcProject types
- Pass organization parameter in apply_default_project_settings()
- Add comprehensive test suite with 8 test cases

How it works:
- For nintendo-switch projects, checks if org has "nintendo-switch" in enabled_console_platforms
- If yes: adds nintendo-private source
- If no: skips nintendo-private source
- Non-restricted sources (ios, microsoft, etc.) are added without access checks

Benefits:
- Explicit platform mapping in DEFAULT_SYMBOL_SOURCES (follows existing pattern)
- Organization access control for console platform sources
- Scalable to other console platforms (PlayStation, Xbox, etc.)
- Automatic org fetch from project if not provided

Test coverage:
- Basic platform handling (no platform, unknown, electron, unity)
- Organization auto-fetch from project
- Platform-restricted sources with and without org access
- Non-restricted sources unaffected by console platform access
@vaind vaind force-pushed the feat/platform-restricted-symbol-sources branch from d5d9eed to b73221a Compare October 28, 2025 12:36
vaind added 6 commits October 28, 2025 15:00
The original implementation incorrectly filtered out symbol sources like
"ios" and "android" that don't exist in SENTRY_BUILTIN_SOURCES. These
sources are intentionally included in defaults even though they don't
exist in the config - they get filtered at runtime in sources.py.

Changes:
- Only check platform restrictions for sources that exist in config
- Sources without config entries are included without restriction checks
- Always update project option for recognized platforms (even if empty)
- Fix tests to verify epoch defaults are preserved for unknown platforms

This ensures the existing test_team_projects.py tests continue to pass
while properly implementing platform-restricted sources.
Use typing.cast to explicitly cast source.get('platforms') to list[str] | None
to satisfy mypy's type checking. The cast is safe because we check if the
source exists before accessing it, and the platforms field is always either
None or list[str] per the SENTRY_BUILTIN_SOURCES configuration.
@vaind
Copy link
Contributor Author

vaind commented Oct 29, 2025

Also, just a quick note - I noticed this PR includes both frontend and backend changes. We usually try to keep those separate in different PRs 😉

This now only has backend changes but it looks like CI doesn't remove labels previously assigned.

@vaind
Copy link
Contributor Author

vaind commented Oct 29, 2025

@sentry review

- Move organization.get_option() call outside loop to avoid repeated DB calls
- Add error handling for Organization.DoesNotExist when fetching from RpcProject
- Add comment explaining optimization

Addresses HIGH and MEDIUM severity issues identified by Seer bot review.
@vaind vaind requested a review from priscilawebdev October 29, 2025 19:30
@vaind vaind marked this pull request as ready for review October 29, 2025 19:30
@vaind vaind requested review from a team as code owners October 29, 2025 19:30
Copy link
Contributor

@loewenheim loewenheim left a comment

Choose a reason for hiding this comment

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

If you remove the definition of the Nintendo source (and change all references to nintendo-private to nintendo, in accordance with the getsentry PR, this is ready to merge.

Co-authored-by: Sebastian Zivota <loewenheim@users.noreply.github.com>
@loewenheim loewenheim added the Trigger: getsentry tests Once code is reviewed: apply label to PR to trigger getsentry tests label Nov 19, 2025
"name": "Nintendo Symbol Server",
"layout": {"type": "native"},
"is_public": False,
"platforms": ["nintendo-switch"],
Copy link
Member

Choose a reason for hiding this comment

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

is this an extra check we want to add? will there be more platforms in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • yes, this check is necessary to only show this server for nintendo-switch projects.
  • there's likely to be another console symbol server in the futer

Comment on lines +5 to +30
SENTRY_BUILTIN_SOURCES_PLATFORM_TEST = {
"public-source-1": {
"id": "sentry:public-1",
"name": "Public Source 1",
"type": "http",
"url": "https://example.com/symbols/",
},
"public-source-2": {
"id": "sentry:public-2",
"name": "Public Source 2",
"type": "http",
"url": "https://example.com/symbols2/",
},
"nintendo": {
"id": "sentry:nintendo",
"name": "Nintendo SDK",
"type": "s3",
"bucket": "nintendo-symbols",
"region": "us-east-1",
"access_key": "test-key",
"secret_key": "test-secret",
"layout": {"type": "native"},
"platforms": ["nintendo-switch"],
},
}

Copy link
Member

Choose a reason for hiding this comment

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

instead of defining this object, can't we just import the SENTRY_BUILTIN_SOURCES?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need it because

  • The "nintendo" source is not defined in this repo - it's in getsentry
  • The real SENTRY_BUILTIN_SOURCES contains public sources (ios, microsoft, etc.) that don't have platform restrictions
  • We need a controlled test fixture with both public sources AND a platform-restricted source to properly test the filtering logic

Comment on lines +100 to +113
"""Nintendo Switch platform should NOT see nintendo if org lacks access"""
# Organization does not have nintendo-switch enabled (default is empty list)

resp = self.get_response(self.organization.slug, qs_params={"platform": "nintendo-switch"})
assert resp.status_code == 200

body = resp.data
source_keys = [source["sentry_key"] for source in body]

# Should NOT see nintendo without console platform access
assert "nintendo" not in source_keys
# Should still see public sources
assert "public-source-1" in source_keys
assert "public-source-2" in source_keys
Copy link
Member

Choose a reason for hiding this comment

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

are we adding an additional security check here? Users should only be able to create a Nintendo Switch project if they have access. Or is the idea that access could later be revoked, in which case they would already have an existing Nintendo Switch project?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes we are. the platform is an API input so if we didn't check for access, anyone could add it just by overriding the request.

Comment on lines +22 to +32
def test_unknown_platform(self):
"""Projects with unknown platforms should keep their epoch defaults"""
project = self.create_project(organization=self.organization, platform="unknown-platform")
# Capture epoch defaults before calling set_default_symbol_sources
epoch_defaults = project.get_option("sentry:builtin_symbol_sources")

set_default_symbol_sources(project, self.organization)

# Should not change the defaults for projects with unknown platforms
sources = project.get_option("sentry:builtin_symbol_sources")
assert sources == epoch_defaults
Copy link
Member

Choose a reason for hiding this comment

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

should we test if the epoch changes, if a known platform is provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is already covered by the existing tests:
test_electron_platform (line 51) - verifies electron gets ios, microsoft, electron
test_unity_platform (line 62) - verifies unity gets ios, microsoft, android, nuget, unity, nvidia, ubuntu
These tests implicitly verify epoch defaults are overridden because they assert platform-specific sources are present.

…ared utility

Move organization_has_console_platform_access to sentry/utils/console_platforms.py
so it can be reused by both builtin_symbol_sources.py and tempest/utils.py.
Also fix platform-restricted tests to use mock settings since nintendo source
is defined in getsentry, not in the default SENTRY_BUILTIN_SOURCES.
@vaind vaind requested a review from a team as a code owner November 27, 2025 10:04
Copy link
Member

@priscilawebdev priscilawebdev left a comment

Choose a reason for hiding this comment

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

LGTM! 👏

has_playstation_access = "playstation" in enabled_platforms

return has_playstation_access
return organization_has_console_platform_access(organization, "playstation")
Copy link
Member

Choose a reason for hiding this comment

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

thank you 💜

vaind added a commit that referenced this pull request Nov 28, 2025
…rontend) (#104116)

## Summary

Frontend changes to support platform-restricted builtin symbol sources.

This PR updates the Debug Files settings page to pass the project's
platform to the builtin symbol sources API endpoint, enabling
platform-based filtering of available symbol sources.

## Changes

### Frontend
- **projectDebugFiles/index.tsx**: Pass `project.platform` query
parameter to the builtin symbol sources API

## Context

This is the **frontend companion** to backend PR #102013 that adds:
- Platform restrictions for builtin symbol sources (e.g., Nintendo
Switch private servers)
- Organization-level access control via `enabledConsolePlatforms`
- Automatic default symbol sources for game engines (Unity, Unreal,
Godot)

## Implementation

The change is minimal - it adds the `platform` query parameter when
fetching builtin symbol sources:

```tsx
platform: project.platform || undefined,
```

This allows the backend to filter symbol sources based on:
1. Platform match (if source has `platforms` restriction)
2. Organization access (for platform-restricted sources)

## Deployment

**Can be merged independently** - This change is backward compatible:
- If backend doesn't have filtering yet: Parameter is ignored, no issues
- If backend has filtering: Platform-based filtering works immediately

---

**Related**: Backend PR #102013 (independent, can merge in any order)
Copy link
Contributor

@mujacica mujacica left a comment

Choose a reason for hiding this comment

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

LGTM

@vaind
Copy link
Contributor Author

vaind commented Dec 1, 2025

The API diff is for added "platforms" string array:
{
  paths: {
    /api/0/projects/{organization_id_or_slug}/{project_id_or_slug}/symbol-sources/: {
      get: {
        responses: {
          200: {
            content: {
              application/json: {
                schema: {
                  items: {
                    oneOf: [
                      {
                        properties: {
+                          platforms: {
+                            type: "array"
+                            items: {
+                              type: "string"
+                            }
+                          }
                        }
                      }
                      {
                        properties: {
+                          platforms: {
+                            type: "array"
+                            items: {
+                              type: "string"
+                            }
+                          }
                        }
                      }
                      {
                        properties: {
+                          platforms: {
+                            type: "array"
+                            items: {
+                              type: "string"
+                            }
+                          }
                        }
                      }
                      ...
                    ]
                  }
                }
              }
            }
          }
        }
      }
      post: {
        responses: {
          201: {
            content: {
              application/json: {
                schema: {
                  oneOf: [
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    ...
                  ]
                }
              }
            }
          }
        }
      }
      put: {
        responses: {
          200: {
            content: {
              application/json: {
                schema: {
                  oneOf: [
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    {
                      properties: {
+                        platforms: {
+                          type: "array"
+                          items: {
+                            type: "string"
+                          }
+                        }
                      }
                    }
                    ...
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}

@vaind vaind merged commit 5efb05c into getsentry:master Dec 1, 2025
65 of 66 checks passed
@vaind vaind deleted the feat/platform-restricted-symbol-sources branch December 1, 2025 14:19
jerryzhou196 pushed a commit that referenced this pull request Dec 1, 2025
…g access control (#102013)

## Summary

Adds support for **platform-restricted builtin symbol sources** with
organization-level access control.

This enables symbol sources to be restricted to specific platforms
(e.g., console platforms like Nintendo Switch) and only accessible to
organizations with the appropriate console platform access enabled.

**Companion PR**:
[getsentry/getsentry#18867](getsentry/getsentry#18867)
- Defines the Nintendo symbol source configuration (credentials, bucket
settings, etc.)

## Changes

### Platform Filtering for Builtin Sources API
-
**[builtin_symbol_sources.py](src/sentry/api/endpoints/builtin_symbol_sources.py)**:
Add platform-based filtering
  - Filter sources by `platform` query parameter
- Check organization's `enabledConsolePlatforms` access for restricted
sources
  - Backward compatible (works without platform parameter)

### Default Symbol Sources Enhancement
-
**[default_symbol_sources.py](src/sentry/api/helpers/default_symbol_sources.py)**:
Enhanced helper for platform-specific defaults
  - Added `nintendo-switch` platform mapping to `nintendo` source
  - Check platform restrictions and org access before adding sources
  - Organization auto-fetched from project if not provided

### Shared Utility
- **[console_platforms.py](src/sentry/utils/console_platforms.py)**: New
utility module
- `organization_has_console_platform_access()` - checks if org has
access to a console platform
  - Used by both builtin sources endpoint and tempest utils

### Schema Update
- **[sources.py](src/sentry/lang/native/sources.py)**: Add `platforms`
field to source schema

### Refactoring
- **[tempest/utils.py](src/sentry/tempest/utils.py)**: Refactored to use
shared `organization_has_console_platform_access` utility
- **[team_projects.py](src/sentry/core/endpoints/team_projects.py)**:
Pass organization to `set_default_symbol_sources()`

## How It Works

### Platform Restrictions

Sources can include a `platforms` key to restrict visibility:

```python
"nintendo": {
    "type": "s3",
    "platforms": ["nintendo-switch"],  # Only visible to nintendo-switch projects
    # ... other config (defined in getsentry)
}
```

**Two-layer filtering:**
1. **Platform match**: Request's platform must match source's
`platforms` list
2. **Org access**: Organization must have platform in
`enabledConsolePlatforms`

### Default Symbol Sources

```python
DEFAULT_SYMBOL_SOURCES = {
    "electron": ["ios", "microsoft", "electron"],
    "unity": ["ios", "microsoft", "android", "nuget", "unity", "nvidia", "ubuntu"],
    "unreal": ["ios", "microsoft", "android", "nvidia", "ubuntu"],
    "godot": ["ios", "microsoft", "android", "nuget", "nvidia", "ubuntu"],
    "nintendo-switch": ["nintendo"],
}
```

## Test Plan

- [x] Unit tests pass
- [x] Pre-commit hooks pass
- [x] Mypy type checking passes
- [x] Verify Nintendo Switch project in enabled org sees the source
- [x] Verify Nintendo Switch project in non-enabled org does NOT see the
source
- [x] Verify non-Nintendo projects never see the source
- [x] Verify public sources still visible to all platforms
- [x] Verify API works without platform parameter (backward
compatibility)

---------

Co-authored-by: Priscila Oliveira <priscilawebdev@gmail.com>
Co-authored-by: Sebastian Zivota <loewenheim@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components Trigger: getsentry tests Once code is reviewed: apply label to PR to trigger getsentry tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants