Source code for flask_unchained.bundles.security.decorators.auth_required_same_user
from flask import abort, request
from flask_principal import Permission, UserNeed
from functools import wraps
from http import HTTPStatus
from .auth_required import auth_required
[docs]def auth_required_same_user(*args, **kwargs):
"""
Decorator for requiring an authenticated user to be the same as the
user in the URL parameters. By default the user url parameter name to
lookup is ``id``, but this can be customized by passing an argument::
@auth_require_same_user('user_id')
@bp.route('/users/<int:user_id>/foo/<int:id>')
def get(user_id, id):
# do stuff
Any keyword arguments are passed along to the @auth_required decorator,
so roles can also be specified in the same was as it, eg::
@auth_required_same_user('user_id', role='ROLE_ADMIN')
Aborts with ``HTTP 403: Forbidden`` if the user-check fails.
"""
auth_kwargs = {}
user_id_parameter_name = 'id'
if not (args and callable(args[0])):
auth_kwargs = kwargs
if args and isinstance(args[0], str):
user_id_parameter_name = args[0]
def wrapper(fn):
@wraps(fn)
@auth_required(**auth_kwargs)
def decorated(*args, **kwargs):
try:
user_id = request.view_args[user_id_parameter_name]
except KeyError:
raise KeyError('Unable to find the user lookup parameter '
f'{user_id_parameter_name} in the url args')
if not Permission(UserNeed(user_id)).can():
abort(HTTPStatus.FORBIDDEN)
return fn(*args, **kwargs)
return decorated
if args and callable(args[0]):
return wrapper(args[0])
return wrapper