2

How do I implement granular ACLs at the template level with Jinja2 and Flask?

Here is what I have without a Jinja2 context dictionary or any prebuilt Flask ACL library. This is my own psuedo ACL where I simply would query a database for a role and get that into my index function.

(source modified from The Flask Mega-Tutorial)

def index():
    user = {'nickname': 'Miguel'}  # fake user
    acl = {'role' : 'Supervisor'}  #<---- hardcoded in lieu of a database call from somewhere
    posts = [  # fake array of posts
        { 
            'author': {'nickname': 'John'}, 
            'body': 'Beautiful day in Portland!' 
        },
        { 
            'author': {'nickname': 'Susan'}, 
            'body': 'The Avengers movie was so cool!' 
        }
    ]
    return render_template("index.html",
                           title='Home',
                           user=user,
                           posts=posts
                           acl=acl) #<----made up acl data


<html>
  <head>
    {% if "Supervisor" in acl.role %}  #<---- using made up ACL data from index
    <title>{{ title }} - Welcome Manager</title>
    {% else %}
    <title>Welcome Employee</title>
    {% endif %}
  </head>
  <body>
      <h1>Hello, {{ user.nickname }}!</h1>
  </body>
</html>

I have looked at a couple of libraries for Flask and ACLs such as this

https://github.com/mikeboers/Flask-ACL

but what I cannot find is how to apply an ACL to a specific item in a rendered template, regardless of any Flask library.

If I have a button named "secret-information," but the page is available to everyone, how can I secure just that button? The render_template function has already been called.

Or, sometimes I may have a redacted set of information that only someone of a certain supervisor level can see, and I don't want to create separate templates.

The only thing that comes close is Jinja2's contextfilter,

http://jinja.pocoo.org/docs/dev/api/#jinja2.runtime.Context

But, I am not sure how to use it.

My way above seems very unmanageable unless there are only a few groups. Larger sets will require nesting groups and priorities.

johnny
  • 19,272
  • 52
  • 157
  • 259
  • I haven't seen a library that can do this. At first glance my approach would be to put a static method on the User model so that you could use the current_user objecf from Flask-Login to check their role. So in your template the code would look like {% if current_user.getACL == 'Super' %}. All of your ACL logic would be in the static method. There may be a better way, but that's what I would try first. – Eric Apr 30 '15 at 19:19
  • How does Jinja2 know how to use that method? Can I pass it a function like that, "current_user.getACL," if I understand you. I am guessing that fine grained security is not the norm. Is that something rarely seen? – johnny Apr 30 '15 at 20:22
  • Check out Flask-Login. One of the main features is a user proxy that gives you access to the current user's object in your python code and in Jinja. You can access the data in your User model directly and then also run functions that are defined in the User model. In my experience, role based access is typically not more than 3-4 roles. – Eric Apr 30 '15 at 21:10
  • Are you wondering how to structure the condition for `{% if some_acl_related_condition %}` or are you wondering how to do it without the condition? – dirn Apr 30 '15 at 22:24
  • @dirn I'm asking how to implement ACL in the first place. I gave a way that looked like it could work, but I do not know. – johnny Apr 30 '15 at 22:37
  • @Eric I cannot see where to do that in the User model. I looked at the documentation. I will have to keep reading. Thanks. – johnny Apr 30 '15 at 22:38
  • I'd recommend taking a look at Flask-Security. It offers [role-based access](https://pythonhosted.org/Flask-Security/features.html#role-identity-based-access). – dirn Apr 30 '15 at 22:42
  • I just found this too, https://pythonhosted.org/Flask-User/ – johnny Apr 30 '15 at 22:46

0 Answers0