Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Commit

Permalink
Merge pull request #111 from postatum/101949350_change_password
Browse files Browse the repository at this point in the history
Handle _hidden_fields in apply_privacy
  • Loading branch information
jstoiko committed Sep 26, 2015
2 parents 2d7828b + c184bd7 commit 469f8f8
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 19 deletions.
7 changes: 5 additions & 2 deletions nefertari/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,19 @@ def str2dict(dotted_str, value=None, separator='.'):
return dict_


def validate_data_privacy(request, data):
def validate_data_privacy(request, data, wrapper_kw=None):
""" Validate :data: contains only data allowed by privacy settings.
:param request: Pyramid Request instance
:param data: Dict containing request/response data which should be
validated
"""
from nefertari import wrappers
if wrapper_kw is None:
wrapper_kw = {}

wrapper = wrappers.apply_privacy(request)
allowed_fields = wrapper(result=data).keys()
allowed_fields = wrapper(result=data, **wrapper_kw).keys()
data = data.copy()
data.pop('_type', None)
not_allowed_fields = set(data.keys()) - set(allowed_fields)
Expand Down
27 changes: 23 additions & 4 deletions nefertari/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ def __call__(self, **kwargs):
request_data['_type'] = self.model_cls.__name__

try:
validate_data_privacy(request, request_data)
validate_data_privacy(
request, request_data,
wrapper_kw={'drop_hidden': False})
except ValidationError as ex:
raise JHTTPForbidden(
'Not enough permissions to update fields: {}'.format(ex))
Expand All @@ -135,6 +137,8 @@ class apply_privacy(object):
Privacy is applied checking model's (got using '_type' key value) fields:
* _public_fields: Fields visible to non-authenticated users.
* _auth_fields: Fields visible to authenticated users.
* _hidden_fields: Fields hidden from all users if `self.drop_hidden`
is True, Otherwise is shown to everyone.
Admin can see all the fields. Whether user is admin, is checked by
calling 'is_admin()' method on 'self.request.user'.
Expand All @@ -156,6 +160,7 @@ def _filter_fields(self, data):

public_fields = set(getattr(model_cls, '_public_fields', None) or [])
auth_fields = set(getattr(model_cls, '_auth_fields', None) or [])
hidden_fields = set(getattr(model_cls, '_hidden_fields', None) or [])
fields = set(data.keys())

user = getattr(self.request, 'user', None)
Expand All @@ -170,6 +175,12 @@ def _filter_fields(self, data):
else:
fields &= public_fields

if self.drop_hidden:
if not self.is_admin:
fields -= hidden_fields
else:
fields.update(hidden_fields)

fields.update(['_type', '_pk', '_self'])
if not isinstance(data, dictset):
data = dictset(data)
Expand All @@ -182,7 +193,10 @@ def _apply_nested_privacy(self, data):
:param data: Dict of data to which privacy is already applied.
"""
kw = {'is_admin': self.is_admin}
kw = {
'is_admin': self.is_admin,
'drop_hidden': self.drop_hidden,
}
for key, val in data.items():
if is_document(val):
data[key] = apply_privacy(self.request)(result=val, **kw)
Expand All @@ -194,6 +208,8 @@ def _apply_nested_privacy(self, data):
def __call__(self, **kwargs):
from nefertari.utils import issequence
result = kwargs['result']
self.drop_hidden = kwargs.get('drop_hidden', True)

if not isinstance(result, dict):
return result
data = result.get('data', result)
Expand All @@ -204,8 +220,11 @@ def __call__(self, **kwargs):
user = getattr(self.request, 'user', None)
self.is_admin = user is not None and type(user).is_admin(user)
if issequence(data) and not isinstance(data, dict):
kwargs = {'is_admin': self.is_admin}
data = [apply_privacy(self.request)(result=d, **kwargs)
kw = {
'is_admin': self.is_admin,
'drop_hidden': self.drop_hidden
}
data = [apply_privacy(self.request)(result=d, **kw)
for d in data]
else:
data = self._filter_fields(data)
Expand Down
69 changes: 56 additions & 13 deletions tests/test_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ def test_apply_request_privacy_valid(self, mock_validate):
except Exception:
raise Exception('Unexpected error')
mock_validate.assert_called_once_with(
4, {'zoo': 1, '_type': 'Foo'})
4, {'zoo': 1, '_type': 'Foo'},
wrapper_kw={'drop_hidden': False})

@patch('nefertari.utils.validate_data_privacy')
def test_apply_request_privacy_invalid(self, mock_validate):
Expand All @@ -169,7 +170,8 @@ def test_apply_request_privacy_invalid(self, mock_validate):
expected = 'Not enough permissions to update fields: boo'
assert str(ex.value) == expected
mock_validate.assert_called_once_with(
4, {'zoo': 1, '_type': 'Foo'})
4, {'zoo': 1, '_type': 'Foo'},
wrapper_kw={'drop_hidden': False})

@patch('nefertari.wrappers.obj2dict')
def test_wrap_in_dict_no_meta_dict(self, mock_obj):
Expand Down Expand Up @@ -355,7 +357,8 @@ def test_no_data(self):
def test_item_non_auth(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=None)
filtered = wrappers.apply_privacy(request)(result=self.model_test_data)
Expand All @@ -366,7 +369,8 @@ def test_item_non_auth(self, mock_eng):
def test_item_no_request(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
filtered = wrappers.apply_privacy(None)(result=self.model_test_data)
assert list(sorted(filtered.keys())) == [
Expand All @@ -376,7 +380,8 @@ def test_item_no_request(self, mock_eng):
def test_item_auth(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
filtered = wrappers.apply_privacy(request)(
Expand All @@ -388,7 +393,8 @@ def test_item_auth(self, mock_eng):
def test_item_auth_calculated(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls

class User(object):
Expand All @@ -405,7 +411,8 @@ def is_admin(self, obj):
def test_item_admin(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
filtered = wrappers.apply_privacy(request)(
Expand Down Expand Up @@ -455,7 +462,8 @@ def test_nested_data_not_dict(self, mock_eng):
def test_item_admin_calculated(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls

class User(object):
Expand Down Expand Up @@ -488,7 +496,8 @@ def test_item_no_document_cls(self, mock_eng):
def test_item_no_fields(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=[])
_auth_fields=[],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
filtered = wrappers.apply_privacy(request)(
Expand All @@ -499,7 +508,8 @@ def test_item_no_fields(self, mock_eng):
def test_collection(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
result = {
Expand All @@ -518,12 +528,14 @@ def test_collection(self, mock_eng):
def test_apply_nested_privacy_dict(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
data = {'owner': self.model_test_data}
wrapper = wrappers.apply_privacy(request)
wrapper.is_admin = False
wrapper.drop_hidden = False
filtered = wrapper._apply_nested_privacy(data)
assert list(filtered.keys()) == ['owner']
owner = filtered['owner']
Expand All @@ -534,12 +546,14 @@ def test_apply_nested_privacy_dict(self, mock_eng):
def test_apply_nested_privacy_list(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'])
_auth_fields=['id'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
data = {'owner': [self.model_test_data]}
wrapper = wrappers.apply_privacy(request)
wrapper.is_admin = False
wrapper.drop_hidden = False
filtered = wrapper._apply_nested_privacy(data)
assert list(filtered.keys()) == ['owner']
owner = filtered['owner'][0]
Expand All @@ -550,7 +564,8 @@ def test_apply_nested_privacy_list(self, mock_eng):
def test_simple_call_with_nested(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id', 'creator'])
_auth_fields=['id', 'creator'],
_hidden_fields=[])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
data = {
Expand All @@ -576,3 +591,31 @@ def test_simple_call_with_nested(self, mock_eng):
}

}

@patch('nefertari.wrappers.engine')
def test_hidden_fields_drop(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id', 'name'],
_hidden_fields=['name'])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
filtered = wrappers.apply_privacy(request)(
result=self.model_test_data, is_admin=False,
drop_hidden=True)
assert list(sorted(filtered.keys())) == [
'_pk', '_self', '_type', 'id']

@patch('nefertari.wrappers.engine')
def test_hidden_fields_not_drop(self, mock_eng):
document_cls = Mock(
_public_fields=['name', 'desc'],
_auth_fields=['id'],
_hidden_fields=['name'])
mock_eng.get_document_cls.return_value = document_cls
request = Mock(user=Mock())
filtered = wrappers.apply_privacy(request)(
result=self.model_test_data, is_admin=False,
drop_hidden=False)
assert list(sorted(filtered.keys())) == [
'_pk', '_self', '_type', 'id', 'name']

0 comments on commit 469f8f8

Please sign in to comment.