feat(dashboard_rbac): add support for related roles (#13035)

This commit is contained in:
Amit Miran 2021-02-15 10:57:37 +02:00 committed by GitHub
parent 10296651fb
commit 312cbf736c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 8 deletions

View File

@ -54,6 +54,7 @@ from superset.dashboards.filters import (
DashboardFavoriteFilter,
DashboardFilter,
DashboardTitleOrSlugFilter,
FilterRelatedRoles,
)
from superset.dashboards.schemas import (
DashboardPostSchema,
@ -192,12 +193,14 @@ class DashboardRestApi(BaseSupersetModelRestApi):
order_rel_fields = {
"slices": ("slice_name", "asc"),
"owners": ("first_name", "asc"),
"roles": ("name", "asc"),
}
related_field_filters = {
"owners": RelatedFieldFilter("first_name", FilterRelatedOwners),
"roles": RelatedFieldFilter("name", FilterRelatedRoles),
"created_by": RelatedFieldFilter("first_name", FilterRelatedOwners),
}
allowed_rel_fields = {"owners", "created_by"}
allowed_rel_fields = {"owners", "roles", "created_by"}
openapi_spec_tag = "Dashboards"
""" Override the name set for this collection of endpoints """

View File

@ -14,7 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Any
# pylint: disable=too-few-public-methods
from typing import Any, Optional
from flask_appbuilder.security.sqla.models import Role
from flask_babel import lazy_gettext as _
@ -29,7 +31,7 @@ from superset.views.base import BaseFilter, get_user_roles, is_user_admin
from superset.views.base_api import BaseFavoriteFilter
class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-methods
class DashboardTitleOrSlugFilter(BaseFilter):
name = _("Title or Slug")
arg_name = "title_or_slug"
@ -45,9 +47,7 @@ class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-
)
class DashboardFavoriteFilter(
BaseFavoriteFilter
): # pylint: disable=too-few-public-methods
class DashboardFavoriteFilter(BaseFavoriteFilter):
"""
Custom filter for the GET list that filters all dashboards that a user has favored
"""
@ -57,7 +57,7 @@ class DashboardFavoriteFilter(
model = Dashboard
class DashboardFilter(BaseFilter): # pylint: disable=too-few-public-methods
class DashboardFilter(BaseFilter):
"""
List dashboards with the following criteria:
1. Those which the user owns
@ -138,3 +138,23 @@ class DashboardFilter(BaseFilter): # pylint: disable=too-few-public-methods
)
return query
class FilterRelatedRoles(BaseFilter):
"""
A filter to allow searching for related roles of a resource.
Use in the api by adding something like:
related_field_filters = {
"roles": RelatedFieldFilter("name", FilterRelatedRoles),
}
"""
name = _("Role")
arg_name = "roles"
def apply(self, query: Query, value: Optional[Any]) -> Query:
role_model = security_manager.role_model
if value:
return query.filter(role_model.name.ilike(f"%{value}%"),)
return query

View File

@ -29,7 +29,7 @@ import yaml
from sqlalchemy.sql import func
from freezegun import freeze_time
from sqlalchemy import and_, or_
from sqlalchemy import and_
from superset import db, security_manager
from superset.models.dashboard import Dashboard
from superset.models.core import FavStar, FavStarClassName
@ -1343,3 +1343,37 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin):
assert response == {
"message": {"metadata.yaml": {"type": ["Must be equal to Dashboard."]}}
}
def test_get_all_related_roles(self):
"""
API: Test get filter related roles
"""
self.login(username="admin")
uri = f"api/v1/dashboard/related/roles"
rv = self.client.get(uri)
assert rv.status_code == 200
response = json.loads(rv.data.decode("utf-8"))
roles = db.session.query(security_manager.role_model).all()
expected_roles = [str(role) for role in roles]
assert response["count"] == len(roles)
response_roles = [result["text"] for result in response["result"]]
for expected_role in expected_roles:
assert expected_role in response_roles
def test_get_filter_related_roles(self):
"""
API: Test get filter related roles
"""
self.login(username="admin")
argument = {"filter": "alpha"}
uri = f"api/v1/dashboard/related/roles?q={prison.dumps(argument)}"
rv = self.client.get(uri)
assert rv.status_code == 200
response = json.loads(rv.data.decode("utf-8"))
assert response["count"] == 1
response_roles = [result["text"] for result in response["result"]]
assert "Alpha" in response_roles