feat: add hooks on set_perm for new data permissions (#20600)

* feat: add hooks on set_perm for new data permissions

* fix lint
This commit is contained in:
Daniel Vaz Gaspar 2022-07-06 09:54:18 +01:00 committed by GitHub
parent 818962cc89
commit f38dd1d42d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 11 deletions

View File

@ -213,7 +213,7 @@ def find_cached_objects_in_session(
return iter([])
uuids = uuids or []
try:
items = set(session)
items = list(session)
except ObjectDeletedError:
logger.warning("ObjectDeletedError", exc_info=True)
return iter(())

View File

@ -40,9 +40,11 @@ from flask_appbuilder.security.sqla.manager import SecurityManager
from flask_appbuilder.security.sqla.models import (
assoc_permissionview_role,
assoc_user_role,
Permission,
PermissionView,
Role,
User,
ViewMenu,
)
from flask_appbuilder.security.views import (
PermissionModelView,
@ -931,7 +933,55 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
return pvm.permission.name in {"can_override_role_permissions", "can_approve"}
def set_perm( # pylint: disable=unused-argument
def on_permission_after_insert(
self, mapper: Mapper, connection: Connection, target: Permission
) -> None:
"""
Hook that allows for further custom operations when a new permission
is created by set_perm.
Since set_perm is executed by SQLAlchemy after_insert events, we cannot
create new permissions using a session, so any SQLAlchemy events hooked to
`Permission` will not trigger an after_insert.
:param mapper: The table mapper
:param connection: The DB-API connection
:param target: The mapped instance being persisted
"""
def on_view_menu_after_insert(
self, mapper: Mapper, connection: Connection, target: ViewMenu
) -> None:
"""
Hook that allows for further custom operations when a new ViewMenu
is created by set_perm.
Since set_perm is executed by SQLAlchemy after_insert events, we cannot
create new view_menu's using a session, so any SQLAlchemy events hooked to
`ViewMenu` will not trigger an after_insert.
:param mapper: The table mapper
:param connection: The DB-API connection
:param target: The mapped instance being persisted
"""
def on_permission_view_after_insert(
self, mapper: Mapper, connection: Connection, target: PermissionView
) -> None:
"""
Hook that allows for further custom operations when a new PermissionView
is created by set_perm.
Since set_perm is executed by SQLAlchemy after_insert events, we cannot
create new pvms using a session, so any SQLAlchemy events hooked to
`PermissionView` will not trigger an after_insert.
:param mapper: The table mapper
:param connection: The DB-API connection
:param target: The mapped instance being persisted
"""
def set_perm(
self, mapper: Mapper, connection: Connection, target: "BaseDatasource"
) -> None:
"""
@ -988,12 +1038,14 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
permission_table.insert().values(name=permission_name)
)
permission = self.find_permission(permission_name)
self.on_permission_after_insert(mapper, connection, permission)
if not view_menu:
view_menu_table = (
self.viewmenu_model.__table__ # pylint: disable=no-member
)
connection.execute(view_menu_table.insert().values(name=view_menu_name))
view_menu = self.find_view_menu(view_menu_name)
self.on_view_menu_after_insert(mapper, connection, view_menu)
if permission and view_menu:
pv = (
@ -1010,6 +1062,10 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
permission_id=permission.id, view_menu_id=view_menu.id
)
)
permission = self.find_permission_view_menu(
permission_name, view_menu_name
)
self.on_permission_view_after_insert(mapper, connection, permission)
def raise_for_access(
# pylint: disable=too-many-arguments,too-many-locals

View File

@ -20,7 +20,7 @@ import time
import unittest
from collections import namedtuple
from unittest import mock
from unittest.mock import Mock, patch
from unittest.mock import Mock, patch, call, ANY
from typing import Any
import jwt
@ -157,6 +157,9 @@ class TestRolePermission(SupersetTestCase):
session.commit()
def test_set_perm_sqla_table(self):
security_manager.on_view_menu_after_insert = Mock()
security_manager.on_permission_view_after_insert = Mock()
session = db.session
table = SqlaTable(
schema="tmp_schema",
@ -172,16 +175,34 @@ class TestRolePermission(SupersetTestCase):
self.assertEqual(
stored_table.perm, f"[examples].[tmp_perm_table](id:{stored_table.id})"
)
self.assertIsNotNone(
security_manager.find_permission_view_menu(
"datasource_access", stored_table.perm
)
pvm_dataset = security_manager.find_permission_view_menu(
"datasource_access", stored_table.perm
)
pvm_schema = security_manager.find_permission_view_menu(
"schema_access", stored_table.schema_perm
)
self.assertIsNotNone(pvm_dataset)
self.assertEqual(stored_table.schema_perm, "[examples].[tmp_schema]")
self.assertIsNotNone(
security_manager.find_permission_view_menu(
"schema_access", stored_table.schema_perm
)
self.assertIsNotNone(pvm_schema)
# assert on permission hooks
view_menu_dataset = security_manager.find_view_menu(
f"[examples].[tmp_perm_table](id:{stored_table.id})"
)
view_menu_schema = security_manager.find_view_menu(f"[examples].[tmp_schema]")
security_manager.on_view_menu_after_insert.assert_has_calls(
[
call(ANY, ANY, view_menu_dataset),
call(ANY, ANY, view_menu_schema),
]
)
security_manager.on_permission_view_after_insert.assert_has_calls(
[
call(ANY, ANY, pvm_dataset),
call(ANY, ANY, pvm_schema),
]
)
# table name change