-
-
Notifications
You must be signed in to change notification settings - Fork 510
roles_accepted and roles_required redirects instead of calling unauthorized_handler #769
Comments
The problem is that Depending on whether or not you're using the application factory pattern, what you need to do looks like this: security = Security(app, datastore)
security._state.unauthorized_handler(your_unauthorized_callback_fn)
# or when using an app factory
security = Security()
security_state = security.init_app(app, datastore)
security_state.unauthorized_handler(your_unauthorized_callback_fn) |
Yeah, your solution works 👍 Thanks! Do I still need @app.login_manager.unauthorized_handler decorator after setting unauthorized_handler explicitly? |
Only if you want to continue using the Personally, I don't find the singular unauthorized_handler granular enough, so in my projects I actually reimplement the roles decorators so that they use The complete solution I use looks like this: roles_accepted (aka one of these roles)from flask import abort
from flask_principal import Permission, RoleNeed
from functools import wraps
from http import HTTPStatus
def roles_accepted(*roles):
"""Decorator which specifies that a user must have at least one of the
specified roles.
Aborts with HTTP: 403 if the user doesn't have at least one of the roles
Example::
@app.route('/create_post')
@roles_accepted('ROLE_ADMIN', 'ROLE_EDITOR')
def create_post():
return 'Create Post'
The current user must have either the `ROLE_ADMIN` role or `ROLE_EDITOR`
role in order to view the page.
:param roles: The possible roles.
"""
def wrapper(fn):
@wraps(fn)
def decorated_view(*args, **kwargs):
perm = Permission(*[RoleNeed(role) for role in roles])
if not perm.can():
abort(HTTPStatus.FORBIDDEN)
return fn(*args, **kwargs)
return decorated_view
return wrapper roles_required (aka all of these roles)from flask import abort
from flask_principal import Permission, RoleNeed
from functools import wraps
from http import HTTPStatus
def roles_required(*roles):
"""Decorator which specifies that a user must have all the specified roles.
Aborts with HTTP 403: Forbidden if the user doesn't have the required roles
Example::
@app.route('/dashboard')
@roles_required('ROLE_ADMIN', 'ROLE_EDITOR')
def dashboard():
return 'Dashboard'
The current user must have both the `ROLE_ADMIN` and `ROLE_EDITOR` roles
in order to view the page.
:param roles: The required roles.
"""
def wrapper(fn):
@wraps(fn)
def decorated_view(*args, **kwargs):
perms = [Permission(RoleNeed(role)) for role in roles]
for perm in perms:
if not perm.can():
abort(HTTPStatus.FORBIDDEN)
return fn(*args, **kwargs)
return decorated_view
return wrapper auth_required (in practice, this is the decorator i use 95% of the time, to check both authentication and authorization)from flask_security.decorators import auth_required as security_auth_required
from functools import wraps
from .roles_accepted import roles_accepted
from .roles_required import roles_required
def auth_required(*decorator_args, **decorator_kwargs):
"""Decorator for requiring an authenticated user, optionally with roles
Roles are passed as keyword arguments, like so:
@auth_required(role='REQUIRE_THIS_ONE_ROLE')
@auth_required(roles=['REQUIRE', 'ALL', 'OF', 'THESE', 'ROLES'])
@auth_required(one_of=['EITHER_THIS_ROLE', 'OR_THIS_ONE'])
One of role or roles kwargs can also be combined with one_of:
@auth_required(role='REQUIRED', one_of=['THIS', 'OR_THIS'])
Aborts with HTTP 401: Unauthorized if no user is logged in, or
HTTP 403: Forbidden if any of the specified role checks fail
"""
required_roles = []
one_of_roles = []
if not (decorator_args and callable(decorator_args[0])):
if 'role' in decorator_kwargs and 'roles' in decorator_kwargs:
raise RuntimeError('specify only one of `role` or `roles` kwargs')
elif 'role' in decorator_kwargs:
required_roles = [decorator_kwargs['role']]
elif 'roles' in decorator_kwargs:
required_roles = decorator_kwargs['roles']
if 'one_of' in decorator_kwargs:
one_of_roles = decorator_kwargs['one_of']
def wrapper(fn):
@wraps(fn)
@security_auth_required('session', 'token')
@roles_required(*required_roles)
@roles_accepted(*one_of_roles)
def decorated(*args, **kwargs):
return fn(*args, **kwargs)
return decorated
# allow using the decorator without parenthesis
if decorator_args and callable(decorator_args[0]):
return wrapper(decorator_args[0])
return wrapper |
the 403 Forbiden is proper, but with a |
The `user` arg was removed from `MailUtil.send_mail` in pallets-eco#690. Co-authored-by: Chris Wagner <jwag.wagner@gmail.com>
If using decorators
roles_accepted
orroles_required
and user don't have the role that is needed - he will be redirected from domain.com/tester to domain.com/ , when it is expected thatunauthorized_handler
should be called. Decoratorlogin_required
works fine. What is the problem? Maybe for roles I need to define another callback?The text was updated successfully, but these errors were encountered: