https://github.com/bprinty/Flask-Authorize
Authorization and access control for Flask
https://github.com/bprinty/Flask-Authorize
Last synced: 3 months ago
JSON representation
Authorization and access control for Flask
- Host: GitHub
- URL: https://github.com/bprinty/Flask-Authorize
- Owner: bprinty
- License: mit
- Created: 2019-09-30T01:26:12.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2023-12-21T10:36:33.000Z (about 1 year ago)
- Last Synced: 2024-03-15T01:23:08.913Z (11 months ago)
- Language: Python
- Homepage: https://flask-authorize.readthedocs.io/en/latest/
- Size: 742 KB
- Stars: 64
- Watchers: 4
- Forks: 12
- Open Issues: 11
-
Metadata Files:
- Readme: README.rst
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - bprinty/Flask-Authorize - Authorization and access control for Flask (Python)
README
|Build status| |Code coverage| |Maintenance yes| |GitHub license| |Documentation Status|
.. |Build status| image:: https://github.com/bprinty/Flask-Authorize/actions/workflows/ci.yml/badge.svg
:target: https://github.com/bprinty/Flask-Authorize/actions/workflows/ci.yml.. |Code coverage| image:: https://codecov.io/gh/bprinty/Flask-Authorize/branch/master/graph/badge.svg
:target: https://codecov.io/gh/bprinty/Flask-Authorize.. |Maintenance yes| image:: https://img.shields.io/badge/Maintained%3F-yes-green.svg
:target: https://github.com/bprinty/Flask-Authorize/graphs/commit-activity.. |GitHub license| image:: https://img.shields.io/github/license/bprinty/Flask-Authorize.svg
:target: https://github.com/bprinty/Flask-Authorize/blob/master/LICENSE.. |Documentation Status| image:: https://readthedocs.org/projects/flask-authorize/badge/?version=latest
:target: http://flask-authorize.readthedocs.io/?badge=latest============================
Flask-Authorize
============================Flask-Authorize is a Flask extension designed to simplify the process of incorporating Access Control Lists (ACLs) and Role-Based Access Control (RBAC) into applications housing sensitive data, allowing developers to focus on the actual code for their application instead of logic for enforcing permissions. It uses a unix-like permissions scheme for enforcing access permissions on existing content, and also provides mechanisms for globally enforcing permissions throughout an application.
Installation
============To install the latest stable release via pip, run:
.. code-block:: bash
$ pip install Flask-Authorize
Alternatively with easy_install, run:
.. code-block:: bash
$ easy_install Flask-Authorize
To install the bleeding-edge version of the project (not recommended):
.. code-block:: bash
$ git clone http://github.com/bprinty/Flask-Authorize.git
$ cd Flask-Authorize
$ python setup.py installUsage
=====Below details a minimal example showcasing how to use the extension. First, to set up the flask application with extensions:
.. code-block:: python
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from flask_authorize import Authorizeapp = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
login = LoginManager(app)
authorize = Authorize(app)Defining database models:
.. code-block:: python
from flask_authorize import RestrictionsMixin, AllowancesMixin
from flask_authorize import PermissionsMixin# mapping tables
UserGroup = db.Table(
'user_group', db.Model.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('group_id', db.Integer, db.ForeignKey('groups.id'))
)UserRole = db.Table(
'user_role', db.Model.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('role_id', db.Integer, db.ForeignKey('roles.id'))
)# models
class User(db.Model):
__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)# `roles` and `groups` are reserved words that *must* be defined
# on the `User` model to use group- or role-based authorization.
roles = db.relationship('Role', secondary=UserRole)
groups = db.relationship('Group', secondary=UserGroup)class Group(db.Model, RestrictionsMixin):
__tablename__ = 'groups'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)class Role(db.Model, AllowancesMixin):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)class Article(db.Model, PermissionsMixin):
__tablename__ = 'articles'
__permissions__ = dict(
owner=['read', 'update', 'delete', 'revoke'],
group=['read', 'update'],
other=['read']
)id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), index=True, nullable=False)Defining endpoint actions:
.. code-block:: python
from flask import jsonify
from werkzeug import NotFound, Unauthorized@app.route('/articles', methods=['POST'])
@login.logged_in
@authorize.create(Article)
def article():
article = Article(**request.json)
db.session.add(article)
db.session.commit()
return jsonify(msg='Created Article'), 200@app.route('/articles/', methods=['GET', 'PUT', 'DELETE'])
@login.logged_in
def single_article(ident):
article = db.session.query(Article).filter_by(id=ident).first()
if not article:
raise NotFoundif request.method == 'GET':
# check if the current user is authorized to read the article
if not authorize.read(article):
raise Unauthorizedreturn jsonify(id=article.id, name=article.name), 200
elif request.method == 'PUT':
# check if the current user is authorized to update to the article
if not authorize.update(article):
raise Unauthorizedfor key, value in request.json.items():
setattr(article, key, value)
db.session.commit()return jsonify(id=article.id, name=article.name), 200
elif request.method == 'DELETE':
# check if the current user is associated with the 'admin' role
if not authorize.delete(article) or \
not authorize.has_role('admin'):
raise Unauthorizeddb.session.delete(article)
db.session.commit()return
@app.route('/articles//revoke', methods=['POST'])
@login.logged_in
def revoke_article(ident):
article = db.session.query(Article).filter_by(id=ident).first()
if not article:
raise NotFound# check if the current user can revoke the article
if not authorize.revoke(article):
raise Unauthorizedarticle.revoked = True
db.session.commit()return
Additionally, if you've configured your application to dispatch request processing to API functions, you can use the ``authorize`` extension object as a decorator:
.. code-block:: python
@authorize.create(Article)
def create_article(name):
article = Article(**request.json)
db.session.add(article)
db.session.commit()
return article@authorize.read
def read_article(article):
return article@authorize.update
def update_article(article, **kwargs):
for key, value in request.json.items():
setattr(article, key, value)
db.session.commit()
return article@authorize.delete
def delete_article(article):
db.session.delete(article)
return@authorize.revoke
def revoke_article(article):
article.revoke = True
db.session.commit()
return@authorize.has_role('admin')
def get_admin_articles():
passUsing the extension as a decorator goes a long way in removing boilerplate associated with permissions checking. Additionally, using the ``authorize`` extension object as a decorator will implicitly check the current user's access to each argument or keyword argument to the function. For example, if your method takes two ``Article`` objects and merges them into one, you can add permissions for both operations like so:
.. code-block:: python
@authorize.read
@authorize.create(Article)
def merge_articles(article1, article2):
new_article = Article(name=article1.name + article.2.name)
db.session.add(new_article)
db.session.delete(article1, article2)
db.session.commit()
return new_articleThis function will ensure that the current user has read access to both articles and also create permissions on the **Article** model itself. If the authorization criteria aren't satisfied, an ``Unauthorized`` error will be thrown.
Finally, the ``authorize`` operator is also available in Jinja templates:
.. code-block:: html
{% if authorize.create('articles') %}
Create Article
{% endif %}
{% for article in articles %}
{% if authorize.read(article) %}
{{ article.name }}
{% if authorize.update(article) %}
Update Article
{% endif %}
{% if authorize.in_group('admins') %}
Delete Article
{% endif %}{% endif %}
{% endfor %}Documentation
=============For more detailed documentation, see the `Docs `_.
Questions/Feedback
==================File an issue in the `GitHub issue tracker `_.