Issue
I'm trying to create a role wrapper which will allow me to restrict certain pages and content for different users. I already have methods implemented for checking this, but the wrapper/decorator for implementing this fails and sometimes doesn't, and I have no idea of what the cause could be.
I've searched around looking for a conclusive reason as to what is causing this problem, but unfortunately, Flask's tracebacks do not give a conclusive reason or solution, like most other searches I come up with do.
I'm using Flask-Login, Flask-Migrate, and Flask-SQLAlchemy to manage my web application, I've looked into different methods of applying RBAC, but they all required seemingly complicated changes to my database models, and I felt that my method would have a higher chance of working in the long run.
Here is my simplified code (I can provide the full application if requested). Below that is the full traceback from the debugger.
Thank you.
routes.py
def require_role(roles=["User"]):
def wrap(func):
def run(*args, **kwargs):
if current_user.is_authenticated:
if current_user.has_roles(roles):
return func(*args, **kwargs)
return abort(401)
return run
return wrap
@app.route('/hidden<id>/history')
@login_required
@require_role(roles=['Admin'])
def hidden_history(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
return render_template('hidden_history.html')
@app.route('/hidden<id>/help')
@login_required
def hidden_help(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
return render_template('hidden_help.html')
@app.route('/hidden<id>/')
@login_required
@require_role(roles=['Hidden'])
def hidden(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
# ...
return render_template('hidden.html')
Traceback (most recent call last)
Traceback (most recent call last):
File "A:\Programming\Python\Flask\xevion.dev\wsgi.py", line 1, in <module>
from app import app, db
File "A:\Programming\Python\Flask\xevion.dev\app\__init__.py", line 18, in <module>
from app import routes, models
File "A:\Programming\Python\Flask\xevion.dev\app\routes.py", line 143, in <module>
@require_role(roles=['Hidden'])
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 1251, in decorator
self.add_url_rule(rule, endpoint, f, **options)
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 67, in wrapper_func
return f(self, *args, **kwargs)
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 1222, in add_url_rule
'existing endpoint function: %s' % endpoint)
AssertionError: View function mapping is overwriting an existing endpoint function: run
Edit: I realize now that it doesn't work when there are more than one calls to the wrapper function. How come?
Solution
So to solve the problem that has been plaguing me for the last couple of hours, I've looked into how the flask_login
module actually works, and after a bit of investigating, I found out that they use an import from functools
called wraps
.
I imported that, copied how flask_login
implemented it essentially, and my app is now working.
def require_role(roles=["User"]):
def wrap(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if current_user.is_authenticated:
if current_user.has_roles(roles):
return func(*args, **kwargs)
return abort(401)
return decorated_view
return wrap
flask_login/utils.py#L264-L273
Answered By - Xevion
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.