Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/holinnn/deny
Python authorization library
https://github.com/holinnn/deny
api authorization falcon fastapi permissions python sanic
Last synced: about 1 month ago
JSON representation
Python authorization library
- Host: GitHub
- URL: https://github.com/holinnn/deny
- Owner: holinnn
- License: mit
- Created: 2022-05-12T11:50:59.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-06-10T12:30:40.000Z (over 2 years ago)
- Last Synced: 2024-09-28T16:41:25.934Z (about 2 months ago)
- Topics: api, authorization, falcon, fastapi, permissions, python, sanic
- Language: Python
- Homepage:
- Size: 87.9 KB
- Stars: 85
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# deny
Library for managing permissions in your Python 3.7+ projects.
For example, it can be used to grant access to some API endpoints based on policies you defined.## Installation
```
pip install deny
```## Usage
First define the permissions needed in your project:
```python
from deny import AutoPermission, Permissionclass ProfilePermissions:
view = Permission(name="view_project")
edit = AutoPermission() # name will be automatically set to "ProjectPermissions.edit"class SessionPermissions:
create = AutoPermission()
delete = AutoPermission()
```Then create policies that will be used to grant the permissions:
```python
from deny import Policy, authorizeclass LoggedInUser:
@authorize(SessionPermissions.delete)
async def can_logout(self):
return Trueclass AnonymousUser(Policy):
@authorize(SessionPermissions.create)
async def can_login(self):
return Trueclass UserPolicy(LoggedInUser, Policy):
def __init__(self, current_user_id: int):
"""You should inject any dependency this policy relies on.
Here we just save the current user ID, but you might need
a connection to a database, a whole user object, etc.
"""
self._current_user_id = current_user_id@authorize(ProfilePermissions.edit)
async def can_edit_profile(self, user_id: int) -> bool:
"""Only current user can edit his own profile."""
return self._current_user_id == user_id@authorize(ProfilePermissions.view)
async def can_view_profile(self, user_id: int) -> bool:
"""Everybody can view the user profiles."""
return True
```Finally create an Ability with the right policy and check if the permissions are granted:
```python
from deny import Abilitydef get_ability(current_user_id: Optional[int]) -> Ability:
if current_user_id:
policy = UserPolicy(current_user_id)
else:
policy = AnonymousUser()return Ability(policy=policy)
################
# Logged in user
ability = get_ability(1)
await ability.can(ProfilePermissions.view, 1) # True
await ability.can(ProfilePermissions.view, 2) # True
await ability.can(ProfilePermissions.edit, 1) # True
await ability.authorize(ProfilePermissions.edit, 1) # does not throw any error
await ability.can(ProfilePermissions.edit, 2) # False
await ability.authorize(ProfilePermissions.edit, 2) # throw an UnauthorizedError
await ability.can(SessionPermission.create) # False
await ability.can(ProfilePermissions.delete) # True################
# Anonymous user
ability = get_ability(None)
await ability.can(ProfilePermissions.view, 1) # False
await ability.can(ProfilePermissions.view, 2) # False
await ability.can(ProfilePermissions.edit, 1) # False
await ability.authorize(ProfilePermissions.edit, 1) # throw an UnauthorizedError
await ability.can(ProfilePermissions.edit, 2) # False
await ability.authorize(ProfilePermissions.edit, 2) # throw an UnauthorizedError
await ability.can(SessionPermission.create) # True
await ability.can(ProfilePermissions.delete) # False
```You can see the full example in [examples/usage.py](https://github.com/holinnn/deny/tree/main/examples/usage.py) (you will need `asyncio` to run it, `pip install asyncio`)
## Web frameworks
Deny can be used with any web framework.
But it comes with some helper functions for [Falcon](https://falcon.readthedocs.io/en/stable/), [Sanic](https://sanic.dev/en/), [FastAPI](https://fastapi.tiangolo.com/) and [Flask](https://flask.palletsprojects.com/).Here is an example for the [Sanic](https://sanic.dev/en/) web framework:
```python
from sanic import Sanic
from sanic.request import Request
from sanic.response import HTTPResponse, jsonfrom deny import Ability, AutoPermission, Policy, authorize as policy_authorize
from deny.errors import UnauthorizedError
from deny.ext.sanic import authorizeclass ProjectPermissions:
view = AutoPermission()class UserPolicy(Policy):
@authorize(ProjectPermissions.view)
async def can_view_project(self, request: Request, id: int) -> bool:
return id == 1app = Sanic("example")
@app.middleware("request")
async def inject_ability(request: Request) -> None:
request.ctx.ability = Ability(policy=UserPolicy())@app.get("/projects/")
@authorize(ProjectPermissions.view)
async def get(request: Request, id: int) -> HTTPResponse:
return json({"id": id})@app.exception(UnauthorizedError)
async def unauthorized_handler(request: Request, exc: Exception):
return json({"error": str(exc)}, status=403)
```You can find the examples for each of those frameworks in the [examples/](https://github.com/holinnn/deny/tree/main/examples) folder from this repository.
## Sync support
By default all the classes provided by `deny` are built to run in an asynchronous environement.
If you run in a synchronous environement (without `async`, `await`), then import from `deny.sync` instead of `deny`.
See [examples/sync.py](https://github.com/holinnn/deny/tree/main/examples/sync.py) for a full example.