{"id":13501318,"url":"https://github.com/holinnn/deny","last_synced_at":"2025-08-11T00:13:40.589Z","repository":{"id":37577390,"uuid":"491492389","full_name":"holinnn/deny","owner":"holinnn","description":"Python authorization library ","archived":false,"fork":false,"pushed_at":"2022-06-10T12:30:40.000Z","size":90,"stargazers_count":85,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-06T20:47:04.835Z","etag":null,"topics":["api","authorization","falcon","fastapi","permissions","python","sanic"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/holinnn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-12T11:50:59.000Z","updated_at":"2024-03-18T19:58:25.000Z","dependencies_parsed_at":"2022-08-31T04:21:24.629Z","dependency_job_id":null,"html_url":"https://github.com/holinnn/deny","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/holinnn/deny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Fdeny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Fdeny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Fdeny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Fdeny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/holinnn","download_url":"https://codeload.github.com/holinnn/deny/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Fdeny/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269809718,"owners_count":24478601,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","authorization","falcon","fastapi","permissions","python","sanic"],"created_at":"2024-07-31T22:01:32.990Z","updated_at":"2025-08-11T00:13:40.515Z","avatar_url":"https://github.com/holinnn.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# deny\n\nLibrary for managing permissions in your Python 3.7+ projects.  \nFor example, it can be used to grant access to some API endpoints based on policies you defined.\n\n## Installation\n\n```\npip install deny\n```\n\n## Usage\n\nFirst define the permissions needed in your project:\n\n```python \nfrom deny import AutoPermission, Permission\n\nclass ProfilePermissions:\n    view = Permission(name=\"view_project\")\n    edit = AutoPermission()  # name will be automatically set to \"ProjectPermissions.edit\"\n\nclass SessionPermissions:\n    create = AutoPermission()\n    delete = AutoPermission()\n```\n\nThen create policies that will be used to grant the permissions:\n\n```python\nfrom deny import Policy, authorize\n\nclass LoggedInUser:\n    @authorize(SessionPermissions.delete)\n    async def can_logout(self):\n        return True\n\n\nclass AnonymousUser(Policy):\n    @authorize(SessionPermissions.create)\n    async def can_login(self):\n        return True\n\n\nclass UserPolicy(LoggedInUser, Policy):\n    def __init__(self, current_user_id: int):\n        \"\"\"You should inject any dependency this policy relies on.\n        Here we just save the current user ID, but you might need \n        a connection to a database, a whole user object, etc.\n        \"\"\"\n        self._current_user_id = current_user_id\n\n    @authorize(ProfilePermissions.edit)\n    async def can_edit_profile(self, user_id: int) -\u003e bool:\n        \"\"\"Only current user can edit his own profile.\"\"\"\n        return self._current_user_id == user_id\n\n    @authorize(ProfilePermissions.view)\n    async def can_view_profile(self, user_id: int) -\u003e bool:\n        \"\"\"Everybody can view the user profiles.\"\"\"\n        return True\n```\n\nFinally create an Ability with the right policy and check if the permissions are granted:\n\n```python\nfrom deny import Ability\n\ndef get_ability(current_user_id: Optional[int]) -\u003e Ability:\n    if current_user_id:\n        policy = UserPolicy(current_user_id)\n    else:\n        policy = AnonymousUser()\n\n    return Ability(policy=policy)\n\n################\n# Logged in user\nability = get_ability(1)\nawait ability.can(ProfilePermissions.view, 1) # True\nawait ability.can(ProfilePermissions.view, 2) # True\nawait ability.can(ProfilePermissions.edit, 1) # True\nawait ability.authorize(ProfilePermissions.edit, 1) # does not throw any error\nawait ability.can(ProfilePermissions.edit, 2) # False\nawait ability.authorize(ProfilePermissions.edit, 2) # throw an UnauthorizedError\nawait ability.can(SessionPermission.create) # False\nawait ability.can(ProfilePermissions.delete) # True\n\n################\n# Anonymous user\nability = get_ability(None)\nawait ability.can(ProfilePermissions.view, 1) # False\nawait ability.can(ProfilePermissions.view, 2) # False\nawait ability.can(ProfilePermissions.edit, 1) # False\nawait ability.authorize(ProfilePermissions.edit, 1) # throw an UnauthorizedError\nawait ability.can(ProfilePermissions.edit, 2) # False\nawait ability.authorize(ProfilePermissions.edit, 2) # throw an UnauthorizedError\nawait ability.can(SessionPermission.create) # True\nawait ability.can(ProfilePermissions.delete) # False\n```\n\nYou 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`)\n\n\n## Web frameworks\n\nDeny can be used with any web framework.  \nBut 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/).\n\nHere is an example for the [Sanic](https://sanic.dev/en/) web framework:\n```python \nfrom sanic import Sanic\nfrom sanic.request import Request\nfrom sanic.response import HTTPResponse, json\n\nfrom deny import Ability, AutoPermission, Policy, authorize as policy_authorize\nfrom deny.errors import UnauthorizedError\nfrom deny.ext.sanic import authorize\n\n\n\nclass ProjectPermissions:\n    view = AutoPermission()\n\n\nclass UserPolicy(Policy):\n    @authorize(ProjectPermissions.view)\n    async def can_view_project(self, request: Request, id: int) -\u003e bool:\n        return id == 1\n\n\napp = Sanic(\"example\")\n\n\n@app.middleware(\"request\")\nasync def inject_ability(request: Request) -\u003e None:\n    request.ctx.ability = Ability(policy=UserPolicy())\n\n\n@app.get(\"/projects/\u003cid:int\u003e\")\n@authorize(ProjectPermissions.view)\nasync def get(request: Request, id: int) -\u003e HTTPResponse:\n    return json({\"id\": id})\n\n\n@app.exception(UnauthorizedError)\nasync def unauthorized_handler(request: Request, exc: Exception):\n    return json({\"error\": str(exc)}, status=403)\n```\n\nYou can find the examples for each of those frameworks in the [examples/](https://github.com/holinnn/deny/tree/main/examples) folder from this repository.\n\n\n## Sync support\n\nBy default all the classes provided by `deny` are built to run in an asynchronous environement.  \nIf you run in a synchronous environement (without `async`, `await`), then import from `deny.sync` instead of `deny`.  \nSee [examples/sync.py](https://github.com/holinnn/deny/tree/main/examples/sync.py) for a full example.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fholinnn%2Fdeny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fholinnn%2Fdeny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fholinnn%2Fdeny/lists"}