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
Python's simple login system concept - Advanced level

Simple login system with Python - For beginners Creating a simple login system using python for beginners this repository aims to provide a simple ove

Low_Scarlet 1 Dec 13, 2021
Web authentication testing framework

What is this This is a framework designed to test authentication for web applications. While web proxies like ZAProxy and Burpsuite allow authenticate

OWASP 88 Jan 01, 2023
Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack

Microsoft365_devicePhish Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack This is a simple proof-of-concept script that allows an at

Optiv Security 76 Jan 02, 2023
A flask extension for managing permissions and scopes

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

Anurag Chaudhury 49 Dec 23, 2022
This is a Python library for accessing resources protected by OAuth 2.0.

This is a client library for accessing resources protected by OAuth 2.0. Note: oauth2client is now deprecated. No more features will be added to the l

Google APIs 787 Dec 13, 2022
FastAPI extension that provides JWT Auth support (secure, easy to use, and lightweight)

FastAPI JWT Auth Documentation: https://indominusbyte.github.io/fastapi-jwt-auth Source Code: https://github.com/IndominusByte/fastapi-jwt-auth Featur

Nyoman Pradipta Dewantara 468 Jan 01, 2023
Django Auth Protection This package logout users from the system by changing the password in Simple JWT REST API.

Django Auth Protection Django Auth Protection This package logout users from the system by changing the password in REST API. Why Django Auth Protecti

Iman Karimi 5 Oct 26, 2022
Phishing Abusing Microsoft 365 OAuth Authorization Flow

Microsoft365_devicePhish Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack This is a simple proof-of-concept script that allows an at

bigb0ss 11 Dec 11, 2022
Implements authentication and authorization as FastAPI dependencies

FastAPI Security Implements authentication and authorization as dependencies in FastAPI. Features Authentication via JWT-based OAuth 2 access tokens a

Jacob Magnusson 111 Jan 07, 2023
A simple Boilerplate to Setup Authentication using Django-allauth 🚀

A simple Boilerplate to Setup Authentication using Django-allauth, with a custom template for login and registration using django-crispy-forms.

Yasser Tahiri 13 May 13, 2022
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
RSA Cryptography Authentication Proof-of-Concept

RSA Cryptography Authentication Proof-of-Concept This project was a request by Structured Programming lectures in Computer Science college. It runs wi

Dennys Marcos 1 Jan 22, 2022
Includes Automation and Personal Projects

Python Models, and Connect Forclient & OpenCv projects Completed Automation** Alarm (S

tushar malhan 1 Jan 15, 2022
A full Rest-API With Oauth2 and JWT for request & response a JSON file Using FastAPI and SQLAlchemy 🔑

Pexon-Rest-API A full Rest-API for request & response a JSON file, Building a Simple WorkFlow that help you to Request a JSON File Format and Handling

Yasser Tahiri 15 Jul 22, 2022
A Python library to create and validate authentication tokens

handshake A Python library to create and validate authentication tokens. handshake is used to generate and validate arbitrary authentication tokens th

0 Apr 26, 2022
Complete Two-Factor Authentication for Django providing the easiest integration into most Django projects.

Django Two-Factor Authentication Complete Two-Factor Authentication for Django. Built on top of the one-time password framework django-otp and Django'

Bouke Haarsma 1.3k Jan 04, 2023
Todo app with authentication system.

todo list web app with authentication system. User can register, login, logout. User can login and create, delete, update task Home Page here you will

Anurag verma 3 Aug 18, 2022
Cack facebook tidak login

Cack facebook tidak login

Angga Kurniawan 5 Dec 12, 2021
Login-python - Login system made in Python, using native libraries

login-python Sistema de login feito 100% em Python, utilizando bibliotecas nativ

Nicholas Gabriel De Matos Leal 2 Jan 28, 2022
FastAPI-Login tries to provide similar functionality as Flask-Login does.

FastAPI-Login FastAPI-Login tries to provide similar functionality as Flask-Login does. Installation $ pip install fastapi-login Usage To begin we hav

417 Jan 07, 2023