Repository navigation
fix(security): handle a missing dataset permission when renaming a database - #45864
aminghadersohi wants to merge 2 commits into
Conversation
…tabase Renaming a database rewrites each dataset view menu with an UPDATE matched by the old name, then reads the row back by the new name. When the dataset had no view menu under the old name the UPDATE matched nothing, the read returned None and the request failed with "'NoneType' object has no attribute 'id'". Create the dataset permission under the new name in that case, and make the connection-level permission/view-menu lookups return None instead of raising.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #45864 +/- ##
=======================================
Coverage 84.70% 84.70%
=======================================
Files 3069 3069
Lines 205610 205668 +58
Branches 48330 48342 +12
=======================================
+ Hits 174157 174208 +51
- Misses 28160 28164 +4
- Partials 3293 3296 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
End-to-end check at a248826 against the merge-base, in-process with the real app (sqlite metadata DB, real
Correction to what I first wrote here about the chart perm: it only goes wrong when the dataset is already loaded in the session before the database rename (my harness kept a single app context across the PUT). A rename from a fresh request session is not affected on master. The same stale-instance problem is reachable on master through asset import (a dataset renamed or moved to another schema leaves its chart on the old perm); it is fixed separately in #45874, which does not touch the lines this PR edits. |
SUMMARY
Renaming a database (
PUT /api/v1/database/<id>) can fail with'NoneType' object has no attribute 'id'when one of its datasets has nodatasource_accessview menu under the old database name.Root cause: on rename,
database_after_updatecalls_update_vm_datasources_access, which renames each dataset view menu withUPDATE ab_view_menu ... WHERE name = <old dataset perm>and then reads the row back by the new name with_find_view_menu_on_sqla_event. If the old dataset view menu is missing, the UPDATE matches no row, the read returnsNone, andview_menu_.idraises. When the old database permission is missing too, the logs show "Could not find previous database permission ..." first; that warning comes from the database path, which already recreates the permission and does not crash. The crash comes from the dataset path.Changes in
superset/security/manager.py:_update_vm_datasources_access: if the renamed dataset view menu cannot be read back, log a warning and create thedatasource_accesspermission under the new name (this matches what_update_dataset_permalready does when the old dataset permission is missing). The dataset and chartpermcolumns are still rewritten._find_permission_on_sqla_event/_find_view_menu_on_sqla_eventreturnNonewhen no row matches instead of raisingAttributeError.None:_update_vm_database_accessskips the update hook, and_insert_pvm_on_sqla_eventlogs and returns if it cannot read back a row it just inserted (permission, view menu or permission view).Other code that renames a view menu and then reads it back, and why it is safe:
_update_vm_database_access: renames by the id of a permission view it has already found, and skips the read-back when there is none._update_dataset_perm: checks that the old dataset view menu exists before renaming, and inserts a new permission otherwise.semantic_layer_before_updateand its view cascade,semantic_view_before_update: check that the old view menu exists before renaming (insert, or skip, otherwise)._insert_pvm_on_sqla_event: re-reads rows it has just inserted on the same connection. Commita248826c944f79f2a2abcaeb1a857949ce4f5755adds explicit missing-row guards for those reads.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A
TESTING INSTRUCTIONS
test_rename_database_with_missing_permissioncreates a database with one schema-less dataset and a chart, deletes the old database permission, the dataset permission or both, then renames the database. Without the fix, thedatasetanddatabase-and-datasetcases fail withAttributeError: 'NoneType' object has no attribute 'id'. With the fix all three pass, the database and dataset permissions exist under the new name, and the persisted chartpermreferences the renamed database. The database-only case, including the chart assertion, also passes without the source fix.test_database_permission_update_skips_hook_when_view_menu_missinginjects a missing database view-menu readback to pin the defensive guard: the update hook must not receiveNone. A successful update is visible on the same connection, so this is a fault-injection test rather than a natural missing-row scenario.ADDITIONAL INFORMATION