Package: deny
denyLibrary 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.Installationpip install denyUsageFirst define the permissions needed in your project:fromdenyimportAutoPermission,PermissionclassProfilePermissions:view=Permission(name="view_project")edit=AutoPermission()# name will be automatically set to "ProjectPermissions.edit"classSessionPermissions:create=AutoPermission()delete=AutoPermission()Then create policies that will be used to grant the permissions:fromdenyimportPolicy,authorizeclassLoggedInUser:@authorize(SessionPermissions.delete)asyncdefcan_logout(self):returnTrueclassAnonymousUser(Policy):@authorize(SessionPermissions.create)asyncdefcan_logout(self):returnTrueclassUserPolicy(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 needa connection to a database, a whole user object, etc."""self._current_user_id=current_user_id@authorize(ProfilePermissions.edit)asyncdefcan_edit_profile(self,user_id:int)->bool:"""Only current user can edit his own profile."""returnself._current_user_id==user_id@authorize(ProfilePermissions.view)asyncdefcan_view_profile(self,user_id:int)->bool:"""Everybody can view the user profiles."""returnTrueFinally create an Ability with the right policy and check if the permissions are granted:fromdenyimportAbilitydefget_ability(current_user_id:Optional[int])->Ability:ifcurrent_user_id:policy=UserPolicy(current_user_id)else:policy=AnonymousUser()returnAbility(policy=policy)################# Logged in userability=get_ability(1)awaitability.can(ProfilePermissions.view,1)# Trueawaitability.can(ProfilePermissions.view,2)# Trueawaitability.can(ProfilePermissions.edit,1)# Trueawaitability.authorize(ProfilePermissions.edit,1)# does not throw any errorawaitability.can(ProfilePermissions.edit,2)# Falseawaitability.authorize(ProfilePermissions.edit,2)# throw an UnauthorizedErrorawaitability.can(SessionPermission.create)# Falseawaitability.can(ProfilePermissions.delete)# True################# Anonymous userability=get_ability(None)awaitability.can(ProfilePermissions.view,1)# Falseawaitability.can(ProfilePermissions.view,2)# Falseawaitability.can(ProfilePermissions.edit,1)# Falseawaitability.authorize(ProfilePermissions.edit,1)# throw an UnauthorizedErrorawaitability.can(ProfilePermissions.edit,2)# Falseawaitability.authorize(ProfilePermissions.edit,2)# throw an UnauthorizedErrorawaitability.can(SessionPermission.create)# Trueawaitability.can(ProfilePermissions.delete)# FalseYou can see the full example inexamples/usage.py(you will needasyncioto run it,pip install asyncio)Web frameworksDeny can be used with any web framework.But it comes with some helper functions forFalcon,Sanic,FastAPIandFlask.Here is an example for theSanicweb framework:fromsanicimportSanicfromsanic.requestimportRequestfromsanic.responseimportHTTPResponse,jsonfromdenyimportAbility,AutoPermission,Policy,authorizeaspolicy_authorizefromdeny.errorsimportUnauthorizedErrorfromdeny.ext.sanicimportauthorizeclassProjectPermissions:view=AutoPermission()classUserPolicy(Policy):@authorize(ProjectPermissions.view)asyncdefcan_view_project(self,request:Request,id:int)->bool:returnid==1app=Sanic("example")@app.middleware("request")asyncdefinject_ability(request:Request)->None:request.ctx.ability=Ability(policy=UserPolicy())@app.get("/projects/<id:int>")@authorize(ProjectPermissions.view)asyncdefget(request:Request,id:int)->HTTPResponse:returnjson({"id":id})@app.exception(UnauthorizedError)asyncdefunauthorized_handler(request:Request,exc:Exception):returnjson({"error":str(exc)},status=403)You can find the examples for each of those frameworks in theexamples/folder from this repository.Sync supportBy default all the classes provided bydenyare built to run in an asynchronous environement.If you run in a synchronous environement (withoutasync,await), then import fromdeny.syncinstead ofdeny.Seeexamples/sync.pyfor a full example.