A flask extension for managing permissions and scopes

Overview

Flask-Pundit Build Status

A simple flask extension to organize resource authorization and scoping. This extension is heavily inspired by the ruby Pundit library.

Installation

pip install flask-pundit

Initialization

You can initialize the extension in one of 2 ways -

  1. pundit = FlaskPundit(app) where app is the application object.
  2. pundit.init_app(app) after constructing the FlaskPundit object without an app object.

When initializing the extension, you can provide an optional policies_path parameter which tells Flask-Pundit where to find your policy classes. If no value is specified this defaults to policies.

What is this policies_path exactly?

Flask-Pundit expects you to have 1 policy per model class. To find the Policy for a particular model it needs to know where to look. That is the policies_path.

Policies

A policy class defines the 'rules' used to authorize a model. You can write your own policy class as follows:

class PostPolicy():
        def __init__(self, user, post):
                self.user = user
                self.post = post
        
        def get(self):
                return self.user == 'admin' and self.post.id == 1

The user object is the currently 'logged' in user and the post object is the model instance you want to authorize. The get method is an authorization 'action' handler that you might want to execute when a user is trying to read a post.

You could alternatively define your own BasePolicy class and extend it in a similar fashion or use the ApplicationPolicy class provided by the extension in which case the code would be:

from flask_pundit.application_policy import ApplicationPolicy

class PostPolicy(ApplicationPolicy):
        def get(self):
                return self.user == 'admin' and self.record.id == 1

Note that now we're using record inside the method. By inheriting from ApplicationPolicy all instance methods now use record to represent the model instance being authorized.

To authorize a post object inside a resource (or a blueprint or just a app.route decorated function) you would call self.pundit.authorize(post). This will cause flask-pundit to look for the PostPolicy class at policies/post. If you want a different root to be searched, you can specify the policies_path when initializing the extension.

This example shows how to use the authorize method in a single module app.

app = Flask('blog_series')
pundit = FlaskPundit(app)

@app.route('/blogs/<id>')
def read_blog_post(id):
        blog = Post.get_by_id(id)
        if pundit.authorize(post):
                return blog
        return ForbiddenError, 403

The authorize method takes 3 parameters:

  1. A record - This can be either an object or class and corresponds to a 'model' that you're doing the authorization on.

  2. An action - This corresponds to the policy method that you want to invoke for doing the authorization. If no value is provided it defaults to request.method.lowercase(). Thus in the previous snippet the get method of a BlogPolicy object would be invoked.

  3. A user - This is akin to the currently 'logged in' user. If no user object is provided, flask-pundit tries to pick either flask.g.user or flask.g.current_user, whichever is available.

Thus in the above set of examples, invoking authorize executes the get method in the PostPolicy class at policies/post with the record being the post object filtered by id.

Scopes

The authorize method acts more as a true/false guard. On the other hand the policy_scope method returns a 'scoped' version of a model. For example, if you have a page with all posts, you might want to let an admin see all of them but restrict the ones staff users see. This is where you'd want to use policy_scope instead of authorize.

To do so, you need to define a scope method in your policy.

from flask_pundit.application_policy import ApplicationPolicy

class PostPolicy(ApplicationPolicy):
        def get(self):
                return self.user == 'admin' and self.record.id == 1


        def scope(self):
                if self.user == 'admin':
                        return record.all()
                return record.filter_by(author='staff')
        

When you call the policy_scope(model) with a model class (it doesn't make sense to pass an object here), the scope method gets called.

from app import pundit

@app.route('/posts)
def index():
        all_posts = pundit.policy_scope(Post)
        return all_posts

The examples here show how to return all posts for an admin and only staff posts for a staff user.

The policy_scope method takes 2 arguments:

  1. A model - This is the class that is to be 'scoped'.

  2. A user object - This is just like the user object in the authorize case.

Verification

Flask-Pundit has 2 decorators you can use to verify authorize/ policy_scope has been called. They are verify_authorized and verify_policy_scoped.

In a single module app you would use verify_authorized as:

from flask_pundit import verify_authorized
from app import app, pundit

@app.route('/posts/<id>')
@verify_authorized
def read_blog_post(id):
        blog_post = Post.get_by_id(id)
        if pundit.authorize(blog_post):
                return blog_post
        return ForbiddenError, 403

If you remove the call to authorize the decorator will throw a RuntimeError as it expects a call but found none.

The verify_policy_scoped decorator would be used in the exact same way. Using these 2 would prove more useful if you're using something like Flask-Restful where you could specify these as method_decorators in your resource, if you wanted all the methods to be verified.

If you prefer not using decorators you could use pundit._verify_authorized and pundit._verify_policy_scoped directly inside your methods. Calling them directly will return True or False.

Custom Policy class

You could override the policy class lookup behaviour by adding a __policy_class__ property on your models. This should reference the class that you want to be used against this model. For example,

from policies.commenting import CommentingPolicy

class Comment:
        __policy_class__ = CommentingPolicy

Now when doing either authorize or policy_scope against an instance of Comment or the class itself, CommentingPolicy will be used.

License

Licensed under MIT license

Comments
  • Feature/add authorized or scoped decorator

    Feature/add authorized or scoped decorator

    This is just a convenience decorator for me. I’m blanket enforcing that resources do some sort of authorization. I don’t really care if they’re using a scope or direct auth, as long as they’re checking one of the two.

    For the tests I just copied the existing tests you have for those decorators and made sure that it will pass if either function is called.

    I’m happy to add a mention of this decorator into the readme if you agree it’s a good idea.

    If I end up writing anything else while using this, is just creating a pull request a good way to offer back the suggestions? Or would you rather I put in an issue first and ask?

    Thanks!

    opened by dbanty 3
  • Support Flask 1.0.0+

    Support Flask 1.0.0+

    Hello,

    I was just about to try using Flask-pundit which looks amazing and exactly what I've been searching for. However, I'm using Flask 1.0.2 which pip thinks is incompatible with this extension. I see you already changed the requirements to the new version of Flask, will you be uploading a release soon with this support?

    Thanks!

    opened by dbanty 3
  • Unpin Flask requirement

    Unpin Flask requirement

    As Flask releases bug fixes (e.g. 1.0.3), new features (e.g. 1.1.0), etc. that do not break compatibility (according to semver) it would be nice for Pundit to not complain about using newer releases. I've updated setup.py to reflect this.

    opened by dbanty 2
  • Python 3 Support

    Python 3 Support

    Using Python 3.7 I get an IndexError when passing in a resource to the authorize function. It looks like the regex pattern in Python 3 is matching a final, empty string, so when name[-1] is called, it's an error.

    The basic fix is pretty easy, just check if the string is empty in the internal dasherize function and if so, return that empty string.

    I'm fixing it anyway for my own usage, so I'll put in a pull request.

    opened by dbanty 0
  • Remove scope classes and replace them with scope methods

    Remove scope classes and replace them with scope methods

    Scope classes are just unnecessary complexity and can be achieved by a method.

    I still like the idea of having a policy_scopemethod vs just calling authorize with an action as scope. This is primarily because its more indicative that this is 'different' behaviour.

    opened by anurag90x 0
  • Support Flask 2

    Support Flask 2

    Right now Flask Pundit has a dependency on flask. flask<2,>=1.0.2

    https://flask.palletsprojects.com/en/2.0.x/changes/

    The major changes are the drop of official support for Py2 but since this lib already supports py3.7, It might be just fine to bump the dependency

    opened by sergioisidoro 0
Releases(1.0.2)
Owner
Anurag Chaudhury
Anurag Chaudhury
Ready to use and customizable Authentications and Authorisation management for FastAPI ⚡

AuthenticationX 💫 Ready-to-use and customizable Authentications and Oauth2 management for FastAPI ⚡ Source Code: https://github.com/yezz123/AuthX Doc

Yasser Tahiri 404 Dec 27, 2022
FastAPI Simple authentication & Login API using GraphQL and JWT

JeffQL A Simple FastAPI authentication & Login API using GraphQL and JWT. I choose this Name JeffQL cause i have a Low level Friend with a Nickname Je

Yasser Tahiri 26 Nov 24, 2022
Accounts for Django made beautifully simple

Django Userena Userena is a Django application that supplies your Django project with full account management. It's a fully customizable application t

Bread & Pepper 1.3k Sep 18, 2022
User-related REST API based on the awesome Django REST Framework

Django REST Registration User registration REST API, based on Django REST Framework. Documentation Full documentation for the project is available at

Andrzej Pragacz 399 Jan 03, 2023
it's a Django application to register and authenticate users using phone number.

django-phone-auth It's a Django application to register and authenticate users using phone number. CustomUser model created using AbstractUser class.

MsudD 4 Nov 29, 2022
JWT authentication for Pyramid

JWT authentication for Pyramid This package implements an authentication policy for Pyramid that using JSON Web Tokens. This standard (RFC 7519) is of

Wichert Akkerman 73 Dec 03, 2021
An introduction of Markov decision process (MDP) and two algorithms that solve MDPs (value iteration, policy iteration) along with their Python implementations.

Markov Decision Process A Markov decision process (MDP), by definition, is a sequential decision problem for a fully observable, stochastic environmen

Yu Shen 31 Dec 30, 2022
Django-registration (redux) provides user registration functionality for Django websites.

Description: Django-registration provides user registration functionality for Django websites. maintainers: Macropin, DiCato, and joshblum contributor

Andrew Cutler 920 Jan 08, 2023
Imia is an authentication library for Starlette and FastAPI (python 3.8+).

Imia Imia (belarussian for "a name") is an authentication library for Starlette and FastAPI (python 3.8+). Production status The library is considered

Alex Oleshkevich 91 Nov 24, 2022
Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.

Welcome to django-allauth! Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (soc

Raymond Penners 7.7k Jan 03, 2023
User Authentication in Flask using Flask-Login

User-Authentication-in-Flask Set up & Installation. 1 .Clone/Fork the git repo and create an environment Windows git clone https://github.com/Dev-Elie

ONDIEK ELIJAH OCHIENG 31 Dec 11, 2022
Plotly Dash plugin to allow authentication through 3rd party OAuth providers.

dash-auth-external Integrate your dashboards with 3rd parties and external OAuth providers. Overview Do you want to build a Plotly Dash app which pull

James Holcombe 15 Dec 11, 2022
An open source Flask extension that provides JWT support (with batteries included)!

Flask-JWT-Extended Features Flask-JWT-Extended not only adds support for using JSON Web Tokens (JWT) to Flask for protecting views, but also many help

Landon Gilbert-Bland 1.4k Jan 04, 2023
Graphical Password Authentication System.

Graphical Password Authentication System. This is used to increase the protection/security of a website. Our system is divided into further 4 layers of protection. Each layer is totally different and

Hassan Shahzad 12 Dec 16, 2022
Basic auth for Django.

Basic auth for Django.

bichanna 2 Mar 25, 2022
Django server for Travel Mate (Project: nomad)

Travel Mate Server (Project: Nomad) Django 2.0 server for Travel Mate Contribute For new feature request in the app, open a new feature request on the

Travel Mate 41 May 29, 2022
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

Jazzband 3.2k Dec 29, 2022
Extending the Django authentication system with a phone verification step.

Extending the Django authentication system with a phone verification step.

Miguel Grinberg 50 Dec 04, 2022
Python One-Time Password Library

PyOTP - The Python One-Time Password Library PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement tw

PyAuth 2.2k Dec 26, 2022
Automatizando a criação de DAGs usando Jinja e YAML

Automatizando a criação de DAGs no Airflow usando Jinja e YAML Arquitetura do Repo: Pastas por contexto de negócio (ex: Marketing, Analytics, HR, etc)

Arthur Henrique Dell' Antonia 5 Oct 19, 2021