Package: bouncer
Simple Declarative Authentication DSL inspired by Ryan Bates’ excellent
cancan libraryIntroductionMeetbouncer.bounceris your faithful servant. Big, burly, trustworthy – smarter
than he looks, but very effective in what he does. You just have to talk
simply to him. For example:frombouncerimportauthorization_methodfrombouncer.constantsimport*@authorization_methoddefauthorize(user,they):ifuser.is_admin:they.can(MANAGE,ALL)else:they.can(READ,ALL)they.cannot(READ,'TopSecretDocs')defif_author(article):returnarticle.author==userthey.can(EDIT,'Article',if_author)And once you have that setup, you can ask questions like:jonathan=User(name='jonathan',admin=False)marc=User(name='marc',admin=False)article=Article(author=jonathan)printcan(jonathan,EDIT,article)# Trueprintcan(marc,EDIT,article)# False# Can Marc view articles in general?printcan(marc,VIEW,Article)# TrueInstallationpip install bouncerDefining Abilities@authorization_methodUser permissions are defined in a method decorated with@authorize_methodA simple setup looks like so …@authorization_methoddefauthorize(user,they):ifuser.is_admin:they.can(MANAGE,ALL)else:they.can(READ,ALL)defif_author(article):returnarticle.author==userthey.can(EDIT,Article,if_author)If you do not think the “they.can” is pythonic enough you can use theappendsyntax@authorization_methoddefauthorize(user,abilities):ifuser.is_admin:abilities.append(MANAGE,ALL)else:abilities.append(READ,ALL)# See I am using a string hereabilities.append(EDIT,'Article',author=user)Alternative syntaxdictsyntaxYou can also use an alternativedictsyntax. The following is equivalent to above:@authorization_methoddefauthorize(user,they):ifuser.is_admin:they.can(MANAGE,ALL)else:they.can(READ,ALL)they.can(EDIT,Article,author=user)You can add multiple conditions to thedict:they.can(READ,Article,published=True,active=True)Strings instead of classesYou can use strings instead of classes. This means you do not need to import a bunch of files you are not using in initialization@authorization_methoddefauthorize(user,they):ifuser.is_admin:they.can(MANAGE,ALL)else:they.can(READ,ALL)# Notice that I am using a string herethey.can(EDIT,'Article',author=user)Combining RulesYou can (are encouraged to) combine similar rules on a single line:they.can((EDIT,READ,DELETE),(Article,Photo))Combining AbilitiesIt is possible to define multiple abilites for the same resource. This is
particularly useful in combination with thecannotmethodthey.can(MANAGE,ALL)then.cannot(DELETE,('USER','ACCOUNT'))Checking AbilitiesThere are two main ways for checking for authorization.can(and its brothercannot) andensurecanreturns a booleanensurewill raise anAccessDeniedExceptionfrombouncerimportcan,ensurefrombouncer.constantsimport*jonathan=User(name='jonathan',admin=False)# can jonathan edit articles in generalcan(jonathan,EDIT,Article)# ensure jonathan edit articles in general -- otherwise we are going to throw an exceptionensure(jonathan,EDIT,Article)article=Article(author=jonathan)# can jonathan delete this specific articlecan(jonathan,EDIT,article)Decorating your User ModelOptionally, you can add helper methods into your User model by using@authorization_targetFor example:frombouncerimportauthorization_target@authorization_targetclassUser(object):def__init__(self,**kwargs):self.id=kwargs.get('id',1)self.name=kwargs.get('name','')self.admin=kwargs.get('admin',False)pass@propertydefis_admin(self):returnself.adminjonathan=User(name='jonathan',admin=False)marc=User(name='marc',admin=False)article=Article(author=jonathan)printjonathan.can(EDIT,article)# Trueprintmarc.can(EDIT,article)# FalseFlaskIf you use Flask, I am currently working on a Flask extension – follow
its progress here:flask-bouncer.Questions / IssuesFeel free to ping me on twitter:@tushmanor add issues or PRs athttps://github.com/jtushman/bouncer