CI runners have Python 3.11, not 3.10 — specifying language_version: python3.10
causes pre-commit to fail with CalledProcessError when building the mypy
virtualenv. Master doesn't set this; omitting it lets pre-commit use whatever
Python 3.x is available, which handles both local (3.10) and CI (3.11).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The placeholder down_revision "c3d4e5f6a7b8" does not exist in the
Alembic revision chain, causing KeyError on superset db upgrade and
cascading failures across all backend test suites and E2E tests.
Points to the actual master HEAD migration (add_granular_export_permissions).
Also restores language_version: python3.10 for the mypy pre-commit hook.
Without this, pre-commit builds its mypy virtualenv with an older Python,
which then fails on the match statements present in this codebase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove language_version: python3.10 from mypy hook — pre-commit would
fail for developers with only Python 3.11/3.12 installed
- Restore PT004 to ruff ignore list — removing it would flag every
existing pytest fixture across the codebase
python_version = "3.10" in pyproject.toml [tool.mypy] is intentional
(sets mypy's type-checking target to Superset's minimum Python version).
The PYTHONPATH fix for the pylint hook is also intentional.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename migration file to match the table it creates (extension_storage)
- Replace unsafe assert patterns in API with proper None guards
- Add direct import of ExtensionStorage into api.py (removes runtime import)
- Add missing requestBody OpenAPI spec to set_user and set_resource
- Add _key_to_fernet docstring
- Add test_list_resource_filtered_by_category (parity with list_global/list_user)
- Add docs/developer_docs/extensions/extension-points/persistent-storage.md
covering all three scopes, REST API, frontend/backend usage examples,
encryption, key rotation, and data model table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _fernet() now returns MultiFernet built from EXTENSION_STORAGE_ENCRYPTION_KEYS
(falls back to SECRET_KEY). New encryptions use the first key; all keys are
tried on decryption, giving zero-downtime rotation.
- New CLI command: superset rotate-extension-storage-keys
Re-encrypts every is_encrypted row with the current (first) key via
MultiFernet.rotate(). Run after prepending a new key and restarting.
- EXTENSION_STORAGE_ENCRYPTION_KEYS added to config.py with rotation docs.
- Re-enable is_encrypted=true in notebook saves now that rotation is supported.
- Tests: key_rotation_roundtrip and multi_fernet_decrypts_old_key.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- allow_browser_login = True so web UI can authenticate via session
cookie + CSRF token instead of requiring a JWT Bearer header
- Replace db.session.commit() with @transaction() on all write endpoints
to satisfy pylint W9001 and get automatic rollback on error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the Extension Storage Layer (Tier 3) from the architecture proposal:
- New `extension_storage` table and SQLAlchemy model with three storage scopes:
global (shared), user-scoped (per-user), resource-linked (tied to a Superset resource)
- `ExtensionStorageDAO` with upsert semantics, Fernet encryption, and scope isolation
- REST API under `/api/v1/extensions/<publisher>/<name>/storage/persistent/`:
GET/PUT/DELETE for global/<key>, user/<key>, resources/<type>/<uuid>/<key>
GET list endpoints (with ?page & ?page_size pagination) for /global/, /user/, /resources/<type>/<uuid>/
- Alembic migration creating `extension_storage` table
- Unit tests covering all three scopes, upsert, delete, pagination, and encryption
(encrypted roundtrip, Fernet token verification, cross-scope coexistence)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>