factor out datasource_access_request model (#8809)

This commit is contained in:
David Aaron Suddjian 2019-12-17 16:17:49 -08:00 committed by Maxime Beauchemin
parent 9ed4b24533
commit 7a68cb7ca0
7 changed files with 110 additions and 73 deletions

View File

@ -14,4 +14,4 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from . import core, schedules, sql_lab, user_attributes
from . import core, datasource_access_request, schedules, sql_lab, user_attributes

View File

@ -1322,74 +1322,6 @@ class FavStar(Model): # pylint: disable=too-few-public-methods
dttm = Column(DateTime, default=datetime.utcnow)
class DatasourceAccessRequest(Model, AuditMixinNullable):
"""ORM model for the access requests for datasources and dbs."""
__tablename__ = "access_request"
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
datasource_id = Column(Integer)
datasource_type = Column(String(200))
ROLES_BLACKLIST = set(config["ROBOT_PERMISSION_ROLES"])
@property
def cls_model(self) -> Type["BaseDatasource"]:
return ConnectorRegistry.sources[self.datasource_type]
@property
def username(self) -> Markup:
return self.creator()
@property
def datasource(self) -> "BaseDatasource":
return self.get_datasource
@datasource.getter # type: ignore
@utils.memoized
def get_datasource(self) -> "BaseDatasource":
ds = db.session.query(self.cls_model).filter_by(id=self.datasource_id).first()
return ds
@property
def datasource_link(self) -> Optional[Markup]:
return self.datasource.link # pylint: disable=no-member
@property
def roles_with_datasource(self) -> str:
action_list = ""
perm = self.datasource.perm # pylint: disable=no-member
pv = security_manager.find_permission_view_menu("datasource_access", perm)
for role in pv.role:
if role.name in self.ROLES_BLACKLIST:
continue
# pylint: disable=no-member
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_grant={role.name}"
)
link = '<a href="{}">Grant {} Role</a>'.format(href, role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"
@property
def user_roles(self) -> str:
action_list = ""
for role in self.created_by.roles: # pylint: disable=no-member
# pylint: disable=no-member
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_extend={role.name}"
)
link = '<a href="{}">Extend {} Role</a>'.format(href, role.name)
if role.name in self.ROLES_BLACKLIST:
link = "{} Role".format(role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"
# events for updating tags
if is_feature_enabled("TAGGING_SYSTEM"):
sqla.event.listen(Slice, "after_insert", ChartUpdater.after_insert)

View File

@ -0,0 +1,101 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Optional, Type, TYPE_CHECKING
from flask import Markup
from flask_appbuilder import Model
from sqlalchemy import Column, Integer, String
from superset import app, db, security_manager
from superset.connectors.connector_registry import ConnectorRegistry
from superset.models.helpers import AuditMixinNullable
from superset.utils import core as utils
if TYPE_CHECKING:
from superset.connectors.base.models import ( # pylint: disable=unused-import
BaseDatasource,
)
config = app.config
class DatasourceAccessRequest(Model, AuditMixinNullable):
"""ORM model for the access requests for datasources and dbs."""
__tablename__ = "access_request"
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
datasource_id = Column(Integer)
datasource_type = Column(String(200))
ROLES_BLACKLIST = set(config["ROBOT_PERMISSION_ROLES"])
@property
def cls_model(self) -> Type["BaseDatasource"]:
return ConnectorRegistry.sources[self.datasource_type]
@property
def username(self) -> Markup:
return self.creator()
@property
def datasource(self) -> "BaseDatasource":
return self.get_datasource
@datasource.getter # type: ignore
@utils.memoized
def get_datasource(self) -> "BaseDatasource":
ds = db.session.query(self.cls_model).filter_by(id=self.datasource_id).first()
return ds
@property
def datasource_link(self) -> Optional[Markup]:
return self.datasource.link # pylint: disable=no-member
@property
def roles_with_datasource(self) -> str:
action_list = ""
perm = self.datasource.perm # pylint: disable=no-member
pv = security_manager.find_permission_view_menu("datasource_access", perm)
for role in pv.role:
if role.name in self.ROLES_BLACKLIST:
continue
# pylint: disable=no-member
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_grant={role.name}"
)
link = '<a href="{}">Grant {} Role</a>'.format(href, role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"
@property
def user_roles(self) -> str:
action_list = ""
for role in self.created_by.roles: # pylint: disable=no-member
# pylint: disable=no-member
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_extend={role.name}"
)
link = '<a href="{}">Extend {} Role</a>'.format(href, role.name)
if role.name in self.ROLES_BLACKLIST:
link = "{} Role".format(role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"

View File

@ -77,6 +77,7 @@ from superset.exceptions import (
SupersetTimeoutException,
)
from superset.jinja_context import get_template_processor
from superset.models.datasource_access_request import DatasourceAccessRequest
from superset.models.sql_lab import Query, TabState
from superset.models.user_attributes import UserAttribute
from superset.sql_parse import ParsedQuery
@ -117,7 +118,7 @@ config = app.config
CACHE_DEFAULT_TIMEOUT = config["CACHE_DEFAULT_TIMEOUT"]
SQLLAB_QUERY_COST_ESTIMATE_TIMEOUT = config["SQLLAB_QUERY_COST_ESTIMATE_TIMEOUT"]
stats_logger = config["STATS_LOGGER"]
DAR = models.DatasourceAccessRequest
DAR = DatasourceAccessRequest
QueryStatus = utils.QueryStatus
DATABASE_KEYS = [

View File

@ -26,6 +26,7 @@ from superset.connectors.connector_registry import ConnectorRegistry
from superset.connectors.druid.models import DruidDatasource
from superset.connectors.sqla.models import SqlaTable
from superset.models import core as models
from superset.models.datasource_access_request import DatasourceAccessRequest
from .base_tests import SupersetTestCase
@ -83,7 +84,7 @@ def create_access_request(session, ds_type, ds_name, role_name, user_name):
security_manager.add_permission_role(
security_manager.find_role(role_name), ds_perm_view
)
access_request = models.DatasourceAccessRequest(
access_request = DatasourceAccessRequest(
datasource_id=ds.id,
datasource_type=ds_type,
created_by_fk=security_manager.find_user(username=user_name).id,

View File

@ -31,6 +31,7 @@ from superset.connectors.druid.models import DruidCluster, DruidDatasource
from superset.connectors.sqla.models import SqlaTable
from superset.models import core as models
from superset.models.core import Database
from superset.models.datasource_access_request import DatasourceAccessRequest
from superset.utils.core import get_example_database
FAKE_DB_NAME = "fake_db_100"
@ -158,7 +159,7 @@ class SupersetTestCase(TestCase):
return json.loads(resp)
def get_access_requests(self, username, ds_type, ds_id):
DAR = models.DatasourceAccessRequest
DAR = DatasourceAccessRequest
return (
db.session.query(DAR)
.filter(

View File

@ -40,6 +40,7 @@ from superset.connectors.sqla.models import SqlaTable
from superset.db_engine_specs.base import BaseEngineSpec
from superset.db_engine_specs.mssql import MssqlEngineSpec
from superset.models import core as models
from superset.models.datasource_access_request import DatasourceAccessRequest
from superset.models.sql_lab import Query
from superset.utils import core as utils
from superset.views import core as views
@ -55,7 +56,7 @@ class CoreTests(SupersetTestCase):
def setUp(self):
db.session.query(Query).delete()
db.session.query(models.DatasourceAccessRequest).delete()
db.session.query(DatasourceAccessRequest).delete()
db.session.query(models.Log).delete()
self.table_ids = {
tbl.table_name: tbl.id for tbl in (db.session.query(SqlaTable).all())