Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIP-29] Add support for row-level security #8699

Merged
merged 49 commits into from
Feb 22, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ab6ae45
Support and apply filters.
altef Nov 27, 2019
024912e
Added the UI for row level security, and moved it all under SQLA in o…
altef Nov 29, 2019
4d6789f
Added a row level security filter documentation entry.
altef Nov 30, 2019
4152dc2
Accidentally added two new lines to this file.
altef Nov 30, 2019
13f1381
Blacked and iSorted, hopefully. Also, sometimes g.user may not be set.
altef Nov 30, 2019
1100a60
Another isort, and handling g not having a user attribute another way.
altef Nov 30, 2019
76a314a
Let's try this again #CI tests.
altef Nov 30, 2019
86234fa
Adjusted import order for isort; I was sure I'd already done this..
altef Nov 30, 2019
81c2bdb
Row level filters should be wrapped in parentheses in case one contai…
altef Nov 30, 2019
ff80add
Oops, did not think that would change Black's formatting.
altef Nov 30, 2019
aafce2f
Changes as per @mistercrunch.
altef Dec 4, 2019
fd0eaf6
RLS filters are now many-to-many with Roles.
altef Dec 4, 2019
8b6ce72
Updated documentation to reflect RLS filters supporting multiple rows.
altef Dec 4, 2019
5f69386
Let's see what happens when I set it to the previous revision ID
altef Dec 4, 2019
032efcb
Merge branch 'master' into row-level-security
altef Dec 4, 2019
2c252d9
Updated from upstream.
altef Dec 4, 2019
7223da6
There was a pylint error.
altef Dec 4, 2019
84a5009
Added RLS ids to the cache keys; modified documentation; added templa…
altef Dec 9, 2019
7e0e3c8
Merge branch 'master' into row-level-security
altef Dec 9, 2019
253e992
A new migration was merged in.
altef Dec 9, 2019
1b3b8f6
Removed RLS cache key from query_object.
altef Dec 9, 2019
c0ac8e9
RLS added to the cache_key from query_context.
altef Dec 10, 2019
bdfaff6
Merge branch 'master' into row-level-security
altef Dec 11, 2019
041038e
Changes as per @etr2460.
altef Dec 17, 2019
8f0d0d1
Updating entry for RLS pull request.
altef Dec 17, 2019
b402fc2
Merge branch 'master' into row-level-security
altef Dec 17, 2019
e403e9b
Another migration to skip.
altef Dec 17, 2019
9c71f05
Catchup.
altef Jan 10, 2020
78ebc1c
Merge branch 'row-level-security' of https://github.com/altef/incubat…
altef Jan 10, 2020
5a0257c
Changes as per @serenajiang.
altef Jan 10, 2020
9d4bcad
Blacked.
altef Jan 10, 2020
790f395
Blacked and added some attributes to check for.
altef Jan 10, 2020
8adfc89
Changed to a manual query as per @mistercrunch.
altef Jan 14, 2020
2b0ccfc
Blacked.
altef Jan 14, 2020
c68d6e6
Merge branch 'master' into row-level-security
altef Jan 14, 2020
b82acf6
Another migration in the meantime.
altef Jan 14, 2020
d94e6aa
Black wanted some whitespace changes.
altef Jan 14, 2020
f76e953
AttributeError: 'AnonymousUserMixin' object has no attribute 'id'.
altef Jan 14, 2020
3d0822d
Oops, did hasattr backwards.
altef Jan 14, 2020
db9f20a
Merge branch 'master' into row-level-security
altef Jan 23, 2020
524c109
Merge branch 'master' into row-level-security
altef Jan 24, 2020
a2e6b67
Changes as per @mistercrunch.
altef Jan 24, 2020
f581cf7
Doesn't look like text us required here anymore.
altef Jan 24, 2020
085a501
Changes as per @dpgaspar
altef Feb 13, 2020
a625111
Two RLS tests.
altef Feb 13, 2020
a131a72
Row level security is now disabled by default via the feature flag EN…
altef Feb 21, 2020
2ea460b
Merge branch 'master' into row-level-security
altef Feb 21, 2020
1451c05
New head to revise.
altef Feb 21, 2020
5eeb296
Changed the comment.
altef Feb 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added RLS ids to the cache keys; modified documentation; added templa…
…te processing to RLS filters.
  • Loading branch information
altef committed Dec 9, 2019
commit 84a50099ea09603669d868c0a0ee1f4495713cca
2 changes: 0 additions & 2 deletions docs/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ filter for the last 30 days and apply it to a specific role, with a clause like
``date_field > DATE_SUB(NOW(), INTERVAL 30 DAY)``. It can also support multiple
conditions: ``client_id = 6 AND advertiser="foo"``, etc.

You can throw whatever you want in there to define the subset of the table you want the roles in question to have access to.

All relevant ``Row level security filters`` will be ANDed together, so it's
possible to create a situation where two roles conflict in such a way as to
limit a table subset to empty. For example, the filters ``client_id=4`` and
Expand Down
3 changes: 2 additions & 1 deletion superset/common/query_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import simplejson as json

from superset import app
from superset import app, security_manager
from superset.utils import core as utils

# TODO: Type Metrics dictionary with TypedDict when it becomes a vanilla python type
Expand Down Expand Up @@ -130,6 +130,7 @@ def cache_key(self, **extra) -> str:
del cache_dict[k]
if self.time_range:
cache_dict["time_range"] = self.time_range
cache_dict["rls"] = security_manager.get_rls_ids(self.datasource)
json_data = self.json_dumps(cache_dict, sort_keys=True)
return hashlib.md5(json_data.encode("utf-8")).hexdigest()

Expand Down
12 changes: 7 additions & 5 deletions superset/connectors/sqla/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,14 +656,18 @@ def adhoc_metric_to_sqla(self, metric: Dict, cols: Dict) -> Optional[Column]:

return self.make_sqla_column_compatible(sqla_metric, label)

def _get_row_level_filters(self) -> List[str]:
def _get_sqla_row_level_filters(self, template_processor) -> List[str]:
"""
Return the appropriate row level security filters for this table and the current user.

:param BaseTemplateProcessor template_processor: The template processor to apply to the filters.
:returns: A list of SQL clauses to be ANDed together.
:rtype: List[str]
"""
return security_manager.get_row_level_security_filters(self)
return [
text("({})".format(template_processor.process_template(f)))
for f in security_manager.get_rls_filters(self)
]

def get_sqla_query( # sqla
self,
Expand Down Expand Up @@ -844,9 +848,7 @@ def get_sqla_query( # sqla
elif op == "IS NOT NULL":
where_clause_and.append(col_obj.get_sqla_col() != None)

where_clause_and += [
text("({})".format(f)) for f in self._get_row_level_filters()
]
where_clause_and += self._get_sqla_row_level_filters(template_processor)
if extras:
where = extras.get("where")
if where:
Expand Down
22 changes: 21 additions & 1 deletion superset/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class SupersetSecurityManager(SecurityManager):
"ResetPasswordView",
"RoleModelView",
"Security",
"RowLevelSecurityFiltersModelView",
} | USER_MODEL_VIEWS

ALPHA_ONLY_VIEW_MENUS = {"Upload a CSV"}
Expand Down Expand Up @@ -878,7 +879,7 @@ def assert_viz_permission(self, viz: "BaseViz") -> None:

self.assert_datasource_permission(viz.datasource)

def get_row_level_security_filters(self, table):
def get_rls_filters(self, table):
"""
Retrieves the appropriate row level security filters for the current user and the passed table.

Expand All @@ -894,3 +895,22 @@ def get_row_level_security_filters(self, table):
]
except AttributeError:
return []

def get_rls_ids(self, table) -> List[int]:
"""
Retrieves the appropriate row level security filters IDs for the current user and the passed table.

:param table: The table to check against
:returns: A list of IDs.
"""
try:
roles = [role.id for role in g.user.roles]
ids = [
f.id
for f in table.row_level_security_filters
if any(r.id in roles for r in f.roles)
]
ids.sort()
return ids
except AttributeError:
return []
3 changes: 2 additions & 1 deletion superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
from markdown import markdown
from pandas.tseries.frequencies import to_offset

from superset import app, cache, get_css_manifest_files
from superset import app, cache, get_css_manifest_files, security_manager
from superset.constants import NULL_STRING
from superset.exceptions import NullValueException, SpatialException
from superset.utils import core as utils
Expand Down Expand Up @@ -367,6 +367,7 @@ def cache_key(self, query_obj, **extra):
cache_dict["time_range"] = self.form_data.get("time_range")
cache_dict["datasource"] = self.datasource.uid
cache_dict["extra_cache_keys"] = self.datasource.get_extra_cache_keys(query_obj)
cache_dict["rls"] = security_manager.get_rls_ids(self.datasource)
json_data = self.json_dumps(cache_dict, sort_keys=True)
return hashlib.md5(json_data.encode("utf-8")).hexdigest()

Expand Down