Cookbook¶
Authentication¶
Simple authentication - each user has access only to resources created by her.
from blargh import engine
from example.cookies import dm
# 1. Information about resource owner has to be stored somewhere,
# e.g. as a hidden field - so we add user_id field.
from blargh.data_model.fields import Scalar
dm.object('cookie').add_field(Scalar('user_id', hidden=True, type_=int))
dm.object('jar').add_field(Scalar('user_id', hidden=True, type_=int))
# 2. Create a custom storage (e.g. extending DictStorage)
from blargh.exceptions import e400, e401, e404
class AuthDictStorage(engine.DictStorage):
'''Dict storage with authentication.
All resources must have user_id field. This field should never be set
by user in an explicit way, so should stay hidden (or readonly).
This user_id:
* is added to saved data in _write_repr
* is checked in load()
* is added to filter data in selected_ids()
* is available via _current_user_id() method
Note: public methods are documented in engine.DictStorage -
interface & operation stay the same.
'''
def load(self, name, id_):
user_id = self._current_user_id()
instance_data = super().load(name, id_)
if instance_data['user_id'] != user_id:
raise e404('resource does not exist, at least for you, hehehe')
return instance_data
def selected_ids(self, name, data):
user_id = self._current_user_id()
data['user_id'] = user_id
return super().selected_ids(name, data)
def _write_repr(self, instance):
user_id = self._current_user_id()
instance_data = super()._write_repr(instance)
instance_data['user_id'] = user_id
return instance_data
def _current_user_id(self):
'''Return current authenticated user's ID'''
auth_data = engine.world().get_auth()
if not auth_data:
raise e401('authentication is required')
elif 'user_id' not in auth_data:
raise e400('user_id missing in authentication data')
user_id = auth_data['user_id']
return user_id
# 3. TEST
storage = AuthDictStorage({})
engine.setup(dm, storage)
from blargh.api.basic import put, get
# Attempt to create a cookie without authentication
assert put('cookie', 1, {'type': 'muffin'}) == \
({'error': {'code': 'UNAUTHORIZED', 'details': {'msg': 'authentication is required'}}}, 401, {})
# Authenticated cookie creation
assert put('cookie', 1, {'type': 'muffin'}, auth={'user_id': 1}) == \
({'id': 1, 'type': 'muffin'}, 201, {})
# Single cookie access, by cookie creator and by other user
assert get('cookie', 1, auth={'user_id': 1}) == ({'id': 1, 'type': 'muffin'}, 200, {})
assert get('cookie', 1, auth={'user_id': 2}) == \
({'error': {'code': 'OBJECT_DOES_NOT_EXIST',
'details': {'msg': 'resource does not exist, at least for you, hehehe'}}},
404, {})
# Cookie collection access, by cookie creator and by other user
assert get('cookie', auth={'user_id': 1}) == ([{'id': 1, 'type': 'muffin'}], 200, {})
assert get('cookie', auth={'user_id': 2}) == ([], 200, {})
# Attempt to in-direct cookie modification
assert put('jar', 1, {'cookies': [1]}, auth={'user_id': 1}) == ({'id': 1, 'cookies': [1]}, 201, {})
assert put('jar', 1, {'cookies': [1]}, auth={'user_id': 2}) == \
({'error': {'code': 'OBJECT_DOES_NOT_EXIST',
'details': {'msg': 'resource does not exist, at least for you, hehehe'}}},
404, {})
Custom Field class 1¶
[TODO]
Custom Field class 2¶
[TODO]
Read Only Resources¶
[TODO]
Resource access restriction¶
[TODO]
PATCH on a collection¶
[TODO]