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
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
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
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
Strong, Simple, and Precise security for Flask APIs (using jwt)

flask-praetorian Strong, Simple, and Precise security for Flask APIs API security should be strong, simple, and precise like a Roman Legionary. This p

Tucker Beck 321 Dec 18, 2022
Flask Implementation of a login page and some basic functionality.

login_page Flask Implementation of a login page and some basic functionality. How to Run $ chmod +x run.sh setup.sh $ # run setup.sh only if the datab

3 Jun 03, 2021
row level security for FastAPI framework

Row Level Permissions for FastAPI While trying out the excellent FastApi framework there was one peace missing for me: an easy, declarative way to def

Holger Frey 315 Dec 25, 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
Object Moderation Layer

django-oml Welcome to the documentation for django-oml! OML means Object Moderation Layer, the idea is to have a mixin model that allows you to modera

Angel Velásquez 12 Aug 22, 2019
REST implementation of Django authentication system.

djoser REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such

Sunscrapers 2.2k Jan 01, 2023
Kube OpenID Connect is an application that can be used to easily enable authentication flows via OIDC for a kubernetes cluster

Kube OpenID Connect is an application that can be used to easily enable authentication flows via OIDC for a kubernetes cluster. Kubernetes supports OpenID Connect Tokens as a way to identify users wh

7 Nov 20, 2022
This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)

Welcome to django-rest-auth Repository is unmaintained at the moment (on pause). More info can be found on this issue page: https://github.com/Tivix/d

Tivix 2.4k Jan 03, 2023
AddressBookApp - Address Book App in Django

AddressBookApp Application Name Address Book App in Django, 2022 Technologies La

Joshua K 1 Aug 18, 2022
Beihang University Network Authentication Login

北航自动网络认证使用说明 主文件 gw_buaa.py # @file gw_buaa.py # @author Dong # @date 2022-01-25 # @email windcicada 0 Jul 22, 2022

Easy and secure implementation of Azure AD for your FastAPI APIs 🔒 Single- and multi-tenant support.

Easy and secure implementation of Azure AD for your FastAPI APIs 🔒 Single- and multi-tenant support.

Intility 220 Jan 05, 2023
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
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
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
Login qr line & qr image

login-qr-line-qr-image login qr line & qr image python3 & linux ubuntu api source: https://github.com/hert0t/BEAPI-BETA import httpx import qrcode fro

Alif Budiman 1 Dec 27, 2021
Brute force a JWT token. Script uses multithreading.

JWT BF Brute force a JWT token. Script uses multithreading. Tested on Kali Linux v2021.4 (64-bit). Made for educational purposes. I hope it will help!

Ivan Šincek 5 Dec 02, 2022
Quick and simple security for Flask applications

Note This project is non maintained anymore. Consider the Flask-Security-Too project as an alternative. Flask-Security It quickly adds security featur

Matt Wright 1.6k Dec 19, 2022