Skip to content

Commit

Permalink
Support and apply filters.
Browse files Browse the repository at this point in the history
  • Loading branch information
altef committed Nov 27, 2019
1 parent badcf82 commit ab6ae45
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 1 deletion.
10 changes: 9 additions & 1 deletion superset/connectors/sqla/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
from superset.exceptions import DatabaseNotFound
from superset.jinja_context import get_template_processor
from superset.models.annotations import Annotation
from superset.models.core import Database
from superset.models.core import Database, RowLevelSecurityFilter
from superset.models.helpers import QueryResult
from superset.utils import core as utils, import_datasource

Expand Down Expand Up @@ -363,6 +363,7 @@ class SqlaTable(Model, BaseDatasource):
sql = Column(Text)
is_sqllab_view = Column(Boolean, default=False)
template_params = Column(Text)
row_level_security_filters = relationship(RowLevelSecurityFilter, backref="tables", lazy=True)

baselink = "tablemodelview"

Expand Down Expand Up @@ -977,6 +978,13 @@ def _get_top_groups(
return or_(*groups)

def query(self, query_obj: Dict) -> QueryResult:
filters = security_manager.get_row_level_security_filters(self) # Self is the Table model
if len(filters) > 0:
where = query_obj['extras']['where']
if len(where) > 0:
where = "({}) AND ".format(where)
query_obj['extras']['where'] = "{} {}".format(where, " AND ".join(filters))

qry_start_dttm = datetime.now()
query_str_ext = self.get_query_str_extended(query_obj)
sql = query_str_ext.sql
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 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.
"""add_row_level_security_table.py
Revision ID: 415f336a828f
Revises: db4b49eb0782
Create Date: 2019-11-27 21:09:00.650139
"""

# revision identifiers, used by Alembic.
revision = '415f336a828f'
down_revision = 'db4b49eb0782'

from alembic import op
import sqlalchemy as sa


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('row_level_security_filters',
sa.Column('created_on', sa.DateTime(), nullable=True),
sa.Column('changed_on', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('role_id', sa.Integer(), nullable=False),
sa.Column('table_id', sa.Integer(), nullable=False),
sa.Column('clause', sa.Text(), nullable=False),
sa.Column('created_by_fk', sa.Integer(), nullable=True),
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
sa.ForeignKeyConstraint(['role_id'], ['ab_role.id'], ),
sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('row_level_security_filters')
# ### end Alembic commands ###
13 changes: 13 additions & 0 deletions superset/models/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,19 @@ def user_roles(self) -> str:
return "<ul>" + action_list + "</ul>"


class RowLevelSecurityFilter(Model, AuditMixinNullable):
"""
Custom where clauses attached to Tables and Roles.
"""

__tablename__ = "row_level_security_filters"
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
role_id = Column(Integer, ForeignKey("ab_role.id"), nullable=False)
table_id = Column(Integer, ForeignKey("tables.id"), nullable=False)
clause = Column(Text, nullable=False)
# Also look up the backref stuff to go into the table model??


# events for updating tags
if is_feature_enabled("TAGGING_SYSTEM"):
sqla.event.listen(Slice, "after_insert", ChartUpdater.after_insert)
Expand Down
10 changes: 10 additions & 0 deletions superset/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,13 @@ def assert_viz_permission(self, viz: "BaseViz") -> None:
"""

self.assert_datasource_permission(viz.datasource)

def get_row_level_security_filters(self, table):
"""
Retrieves the appropriate row level security filters for the current user and the passed table.
:param table: The table to check against
:returns: A list of clause strings.
"""
roles = [role.id for role in g.user.roles]
return [filter.clause for filter in filter(lambda x: x.role_id in roles, table.row_level_security_filters)]

0 comments on commit ab6ae45

Please sign in to comment.