0

I'm currently working on a web app where I'm using Flask and the Flask-User extension to handle authorization. At the moment I have something like this:

@application.route('/dummy')
@roles_required('Admin')
@login_required
def dummy():

in my views.py file and something like this:

{% if current_user.is_authenticated and current_user.has_roles('Admin') %}

in my templates.

Now, there's been a few times where I've changed my mind in terms of role names and then had to search through the whole project to find and replace those usages. I wanted to define something like a ADMIN_ROLE global that can be accessed inside my views.py or any templates. I'd then just change a config file whenever I wanted to change a role's name.

I tried using my config.py setting ADMIN_ROLE = "Admin" and writing @roles_required(current_app.config['ADMIN_ROLE']) but that gives me the following error:

RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in a way.  To solve
this set up an application context with app.app_context().  See the
documentation for more information.

so that should be wrong. What would be the most correct way to accomplish what I'm trying?

1 Answers1

0

As it says, you can only access config from within the app context. Normally, you do this:

with current_app.app_context():
    admin_role = current_app.config['ADMIN_ROLE']

Because you're doing this in a decorator, I don't believe there's anyway to do that. I would suggest just using a separate module. Now, it doesn't appear that you can use python import statements in jinja, so we'll still use app.config for that. Basically we'll create a module with the desired roles and then import that into the config file and your routes file.

roles.py

ADMIN = "Admin"
USER = "User"

config.py

from roles import ADMIN, USER

ADMIN_ROLE = ADMIN
USER_ROLE = USER

routes.py

from roles import ADMIN, USER

@application.route('/dummy')
@roles_required(ADMIN)
@login_required
def dummy():

views.py

{% if current_user.is_authenticated and current_user.has_roles(app.config['ADMIN_ROLE']) %}
Community
  • 1
  • 1
Allie Fitter
  • 1,689
  • 14
  • 18
  • Thank you, that's a great answer. This seems to be the most logical path to follow and looks like a clean solution, despite we still having to use `app.config` in jinja. Do you agree that what I'm trying to accomplish is better than just writing the string name of the role everywhere? – Gabriel Siqueira May 15 '17 at 21:32
  • Yes, if there is something in code that may change, it's better to change it in one place than several. – Allie Fitter May 15 '17 at 21:36
  • I've ran into a small issue. When using javascript with Jinja, I do something like this: `if($(this).val() !== {{ config['ADMIN_ROLE'] }})` which is translated to `if($(this).val() !== Admin)`, where ADMIN is the name of the variable imported in config.py and not it's value of `"Admin"` – Gabriel Siqueira May 15 '17 at 22:36
  • You need it to be a string literal in JavaScript. Add quotes around the brackets. `"{{ config['ADMIN_ROLE'] }}"` May be typos as I made this comment on my phone. – Allie Fitter May 15 '17 at 22:43
  • Perfect! Thanks! – Gabriel Siqueira May 15 '17 at 23:27
  • Though I would encourage you to not embed JavaScript into html. If you need to pass data to JavaScript, I would suggest assigning it to the value of a `input` element with `type="hidden"`. Then access it by the element's id. – Allie Fitter May 15 '17 at 23:34