I’ve been creating a python-flask web app based on miguel grinbergs excellent tutorial. It’s not a blog, more of an online ordering system, but needs user and admin access, i.e. different options available for different users, which is not included in that tutorial. I’d been researching Flask-Principal which does fine-grained permissions, but it looked a bit complex, since all I wanted was to put and @decorator in front of certain functions to prevent these pages from showing.

A bit more googling and I discovered this excellent snippet. I’ll explain exactly how I implemented it in my code.

1. In my models.py I have a user class, which has a “role” field.

2. In my views.py which contains all the routes / views into the web app, I have the following:

– Import the wraps class:

from functools import wraps 

– A function defintion which will work as a decorator for each view – we can call this with @required_roles. If the required role is not correct, it simply sends a ‘flash’ message which I already have setup and returns the user to the index page. I’m using g.user to handle my current user session.

def required_roles(*roles):
   def wrapper(f):
      @wraps(f)
      def wrapped(*args, **kwargs):
         if get_current_user_role() not in roles:
            flash('Authentication error, please check your details and try again','error')
            return redirect(url_for('index'))
         return f(*args, **kwargs)
      return wrapped
   return wrapper

def get_current_user_role():
   return g.user.role

3. Finally, for each view, I can add the decorator, for example, I’m only letting users with role=admin access the register new user page:

@app.route('/register' , methods=['GET','POST'])
@required_roles('admin')
def register():

4. It’s worth noting that although this will restrict the views that can be accessed, there are other considerations for a complete multi-role web app – i.e. you may have a single view / template with different options – in my case the index page can be access by both standard and admin users, but I want the admin users to be able to see a link to register a new user. If I didn’t do this, ALL users would have a link to the register new user page, but only role=admin users would be able to access it. This is pretty easy to acheive in python-flask with the jinja templating engine. I simply include the link inside an if block as follows:

{% if g.user.role == 'admin' %}
<!--menu link here -->  
{% endif %}