Skip to main content

Library for managing permissions in your Python 3.7+ projects

Project description

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:

from deny import AutoPermission, Permission

class 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:

from deny import Policy, authorize

class LoggedInUser:
    @authorize(SessionPermissions.delete)
    async def can_logout(self):
        return True


class AnonymousUser(Policy):
    @authorize(SessionPermissions.create)
    async def can_logout(self):
        return True


class 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:

from deny import Ability

def 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 (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, Sanic, FastAPI and Flask.

Here is an example for the Sanic web framework:

from sanic import Sanic
from sanic.request import Request
from sanic.response import HTTPResponse, json

from deny import Ability, AutoPermission, Policy, authorize as policy_authorize
from deny.errors import UnauthorizedError
from deny.ext.sanic import authorize



class ProjectPermissions:
    view = AutoPermission()


class UserPolicy(Policy):
    @authorize(ProjectPermissions.view)
    async def can_view_project(self, request: Request, id: int) -> bool:
        return id == 1


app = Sanic("example")


@app.middleware("request")
async def inject_ability(request: Request) -> None:
    request.ctx.ability = Ability(policy=UserPolicy())


@app.get("/projects/<id:int>")
@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/ 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 for a full example.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

deny-0.1.1a2.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

deny-0.1.1a2-py3-none-any.whl (13.4 kB view details)

Uploaded Python 3

File details

Details for the file deny-0.1.1a2.tar.gz.

File metadata

  • Download URL: deny-0.1.1a2.tar.gz
  • Upload date:
  • Size: 9.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.17.9-lp153.2.geab1a2c-default

File hashes

Hashes for deny-0.1.1a2.tar.gz
Algorithm Hash digest
SHA256 7132191d00700d245cc20e7f6bf47bc7c74f4632fb933e520f8fe7fd2224dbc5
MD5 6c3117bda65162950bc85a24bb54f91e
BLAKE2b-256 376e11690752f9809ef82011617f77fcfd19961ba041f034160ee8ee2f87ecab

See more details on using hashes here.

File details

Details for the file deny-0.1.1a2-py3-none-any.whl.

File metadata

  • Download URL: deny-0.1.1a2-py3-none-any.whl
  • Upload date:
  • Size: 13.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.17.9-lp153.2.geab1a2c-default

File hashes

Hashes for deny-0.1.1a2-py3-none-any.whl
Algorithm Hash digest
SHA256 7aaebbedb80638da2292fb8f05031d636a65c06a65221d5a7d068fc04deaef3f
MD5 b7271d67fe2d082473e55d99a618c752
BLAKE2b-256 1199aababe24088701f43521d9ba75026762bca6374976d74537aee4e8c843c3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page