Skip to content

Commit 0bedaed

Browse files
asydorchukmistercrunch
authored andcommitted
Make sure anonymous user with proper permissions can access data (#415)
* Make sure anonymous user with proper permissions can access data * Review fixes: naming changes * Review fixes: add more granular tests for public user dashboard access * Review fixes: test that public user has access only to permitted data sets
1 parent 1d0863a commit 0bedaed

File tree

3 files changed

+83
-9
lines changed

3 files changed

+83
-9
lines changed

caravel/models.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from flask.ext.appbuilder import Model
2424
from flask.ext.appbuilder.models.mixins import AuditMixin
2525
from flask.ext.appbuilder.models.decorators import renders
26-
from flask.ext.babelpkg import lazy_gettext as _
26+
from flask.ext.babelpkg import gettext as _
2727

2828
from pydruid.client import PyDruid
2929
from pydruid.utils.filters import Dimension, Filter
@@ -1240,7 +1240,7 @@ def log_this(cls, f):
12401240
def wrapper(*args, **kwargs):
12411241
user_id = None
12421242
if g.user:
1243-
user_id = g.user.id
1243+
user_id = g.user.get_id()
12441244
d = request.args.to_dict()
12451245
d.update(kwargs)
12461246
slice_id = d.get('slice_id', 0)

caravel/views.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from flask.ext.appbuilder.actions import action
2121
from flask.ext.appbuilder.models.sqla.interface import SQLAInterface
2222
from flask.ext.appbuilder.security.decorators import has_access
23-
from flask.ext.babelpkg import lazy_gettext as _
23+
from flask.ext.babelpkg import gettext as _
2424
from flask_appbuilder.models.sqla.filters import BaseFilter
2525

2626
from pydruid.client import doublesum
@@ -35,10 +35,16 @@
3535
log_this = models.Log.log_this
3636

3737

38+
def get_user_roles():
39+
if g.user.is_anonymous():
40+
return [appbuilder.sm.find_role('Public')]
41+
return g.user.roles
42+
43+
3844
class CaravelFilter(BaseFilter):
3945
def get_perms(self):
4046
perms = []
41-
for role in g.user.roles:
47+
for role in get_user_roles():
4248
for perm_view in role.permissions:
4349
if perm_view.permission.name == 'datasource_access':
4450
perms.append(perm_view.view_menu.name)
@@ -47,7 +53,7 @@ def get_perms(self):
4753

4854
class FilterSlice(CaravelFilter):
4955
def apply(self, query, func): # noqa
50-
if any([r.name in ('Admin', 'Alpha') for r in g.user.roles]):
56+
if any([r.name in ('Admin', 'Alpha') for r in get_user_roles()]):
5157
return query
5258
qry = query.filter(self.model.perm.in_(self.get_perms()))
5359
print(qry)
@@ -56,7 +62,7 @@ def apply(self, query, func): # noqa
5662

5763
class FilterDashboard(CaravelFilter):
5864
def apply(self, query, func): # noqa
59-
if any([r.name in ('Admin', 'Alpha') for r in g.user.roles]):
65+
if any([r.name in ('Admin', 'Alpha') for r in get_user_roles()]):
6066
return query
6167
Slice = models.Slice # noqa
6268
slice_ids_qry = (
@@ -721,12 +727,12 @@ def favstar(self, class_name, obj_id, action):
721727
FavStar = models.FavStar # noqa
722728
count = 0
723729
favs = session.query(FavStar).filter_by(
724-
class_name=class_name, obj_id=obj_id, user_id=g.user.id).all()
730+
class_name=class_name, obj_id=obj_id, user_id=g.user.get_id()).all()
725731
if action == 'select':
726732
if not favs:
727733
session.add(
728734
FavStar(
729-
class_name=class_name, obj_id=obj_id, user_id=g.user.id,
735+
class_name=class_name, obj_id=obj_id, user_id=g.user.get_id(),
730736
dttm=datetime.now()))
731737
count = 1
732738
elif action == 'unselect':

tests/core_tests.py

+69-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from mock import Mock, patch
1313

1414
from flask import escape
15+
from flask_appbuilder.security.sqla import models as ab_models
1516

1617
import caravel
1718
from caravel import app, db, models, utils, appbuilder
@@ -63,17 +64,38 @@ def login_gamma(self):
6364
follow_redirects=True)
6465
assert 'Welcome' in resp.data.decode('utf-8')
6566

67+
def setup_public_access_for_dashboard(self, dashboard_name):
68+
public_role = appbuilder.sm.find_role('Public')
69+
perms = db.session.query(ab_models.PermissionView).all()
70+
for perm in perms:
71+
if perm.permission.name not in (
72+
'can_list',
73+
'can_dashboard',
74+
'can_explore',
75+
'datasource_access'):
76+
continue
77+
if not perm.view_menu:
78+
continue
79+
if perm.view_menu.name not in (
80+
'SliceModelView',
81+
'DashboardModelView',
82+
'Caravel') and dashboard_name not in perm.view_menu.name:
83+
continue
84+
appbuilder.sm.add_permission_role(public_role, perm)
85+
6686

6787
class CoreTests(CaravelTestCase):
6888

6989
def __init__(self, *args, **kwargs):
90+
# Load examples first, so that we setup proper permission-view relations
91+
# for all example data sources.
92+
self.load_examples()
7093
super(CoreTests, self).__init__(*args, **kwargs)
7194
self.table_ids = {tbl.table_name: tbl.id for tbl in (
7295
db.session
7396
.query(models.SqlaTable)
7497
.all()
7598
)}
76-
self.load_examples()
7799

78100
def setUp(self):
79101
pass
@@ -162,6 +184,52 @@ def test_gamma(self):
162184
resp = self.client.get('/dashboardmodelview/list/')
163185
assert "List Dashboard" in resp.data.decode('utf-8')
164186

187+
def test_public_user_dashboard_access(self):
188+
# Try access before adding appropriate permissions.
189+
resp = self.client.get('/slicemodelview/list/')
190+
data = resp.data.decode('utf-8')
191+
assert '<a href="/tablemodelview/edit/3">birth_names</a>' not in data
192+
193+
resp = self.client.get('/dashboardmodelview/list/')
194+
data = resp.data.decode('utf-8')
195+
assert '<a href="/caravel/dashboard/births/">' not in data
196+
197+
resp = self.client.get('/caravel/dashboard/births/')
198+
data = resp.data.decode('utf-8')
199+
assert '[dashboard] Births' not in data
200+
201+
self.setup_public_access_for_dashboard('birth_names')
202+
203+
# Try access after adding appropriate permissions.
204+
resp = self.client.get('/slicemodelview/list/')
205+
data = resp.data.decode('utf-8')
206+
assert '<a href="/tablemodelview/edit/3">birth_names</a>' in data
207+
208+
resp = self.client.get('/dashboardmodelview/list/')
209+
data = resp.data.decode('utf-8')
210+
assert '<a href="/caravel/dashboard/births/">' in data
211+
212+
resp = self.client.get('/caravel/dashboard/births/')
213+
data = resp.data.decode('utf-8')
214+
assert '[dashboard] Births' in data
215+
216+
resp = self.client.get('/caravel/explore/table/3/')
217+
data = resp.data.decode('utf-8')
218+
assert '[explore] birth_names' in data
219+
220+
# Confirm that public doesn't have access to other datasets.
221+
resp = self.client.get('/slicemodelview/list/')
222+
data = resp.data.decode('utf-8')
223+
assert '<a href="/tablemodelview/edit/2">wb_health_population</a>' not in data
224+
225+
resp = self.client.get('/dashboardmodelview/list/')
226+
data = resp.data.decode('utf-8')
227+
assert '<a href="/caravel/dashboard/world_health/">' not in data
228+
229+
resp = self.client.get('/caravel/explore/table/2/', follow_redirects=True)
230+
data = resp.data.decode('utf-8')
231+
assert "You don&#39;t seem to have access to this datasource" in data
232+
165233

166234
SEGMENT_METADATA = [{
167235
"id": "some_id",

0 commit comments

Comments
 (0)