Package: permission
Simple and flexible permission control for Flask app.FeaturesSimple: all you need to do is subclassingRuleandPermissionclass.Flexible: support rule inheritance and bitwise operations(&and|) to build your own rules.Installation$ pip install permissionRuleRulehas 3 methods which can be overrided:base(): define base rule.check(): determine whether this rule should be passed or not.deny(): will be executed whencheck()failed.You should always overridecheck()anddeny()while overridingbase()as needed.PermissionPermissionhas 1 method which can be overrided:rule(): define rule needed by this permissionYou should always overriderule().Permissionhas 2 instance methods you can use in codes:check(): call this to check rule of this permissiondeny(): call this to execute codes whencheck()failedUsageFirst you need to define your own rules by subclassingRulethen
overridecheck()anddeny():# rules.pyfromflaskimportsession,flash,redirect,url_forfrompermissionimportRuleclassUserRule(Rule):defcheck(self):"""Check if there is a user signed in."""return'user_id'insessiondefdeny(self):"""When no user signed in, redirect to signin page."""flash('Sign in first.')returnredirect(url_for('signin'))Then you define permissions by subclassingPermissionand overriderule():# permissions.pyfrompermissionimportPermissionfrom.rulesimportUserRuleclassUserPermission(Permission):"""Only signin user has this permission."""defrule(self):returnUserRule()There are 4 ways to use theUserPermissiondefined above:1. Use as view decoratorfrom.permissionsimportUserPermission@app.route('/settings')@UserPermission()defsettings():"""User settings page, only accessable for sign-in user."""returnrender_template('settings.html')2. Use in view codesfrom.permissionsimportUserPermission@app.route('/settions')defsettings():permission=UserPermission()ifnotpermission.check()returnpermission.deny()returnrender_template('settings.html')3. Use in view codes (using ``with`` statement)from.permissionsimportUserPermission@app.route('/settions')defsettings():withUserPermission():returnrender_template('settings.html')Note: if you don’t raise an exception when the permission check
failed (in other words, a rule’sdeny()will be called),PermissionDeniedExceptionwill be raised in order to stop the
execution of the with-body codes. By the way, you can import this
exception as needed:frompermissionimportPermissionDeniedException4. Use in Jinja2 templatesFirst you need to inject your defined permissions to template context:from.importpermissions@app.context_processordefinject_vars():returndict(permissions=permissions)then in templates:{% if permissions.UserPermission().check() %}<ahref="{{ url_for('new') }}">New</a>{% endif %}Rule InheritanceNeed to say, inheritance here is not the same thing as Python class
inheritance, it’s just means you can use RuleA as the base rule of
RuleB.We achieve this by overridingbase().Let’s say an administrator user should always be a user:# rules.pyfromflaskimportsession,abort,flash,redirect,url_forfrompermissionimportRuleclassUserRule(Rule):defcheck(self):return'user_id'insessiondefdeny(self):flash('Sign in first.')returnredirect(url_for('signin'))classAdminRule(Rule):defbase(self):returnUserRule()defcheck(self):user_id=int(session['user_id'])user=User.query.filter(User.id==user_id).first()returnuseranduser.is_admindefdeny(self):abort(403)Rule Bitwise OperationsRuleA & RuleBmeans it will be passed when both RuleA and RuleB
are passed.RuleA | RuleBmeans it will be passed either RuleA or RuleB is
passed.Let’s say we need to build a forum with Flask. Only the topic creator
and administrator user can edit a topic:First define rules:# rules.pyfromflaskimportsession,abort,flash,redirect,url_forfrompermissionimportRulefrom.modelsimportUser,TopicclassUserRule(Rule):defcheck(self):"""Check if there is a user signed in."""return'user_id'insessiondefdeny(self):"""When no user signed in, redirect to signin page."""flash('Sign in first.')returnredirect(url_for('signin'))classAdminRule(Rule):defbase(self):returnUserRule()defcheck(self):user_id=int(session['user_id'])user=User.query.filter(User.id==user_id).first()returnuseranduser.is_admindefdeny(self):abort(403)classTopicCreatorRule(Rule):def__init__(self,topic):self.topic=topicsuper(TopicCreatorRule,self).__init__()defbase(self):returnUserRule()defcheck(self):returntopic.user_id==session['user_id']defdeny(self):abort(403)then define permissions:# permissions.pyfrompermissionimportPermissionclassTopicAdminPermission(Permission):def__init__(self,topic):self.topic=topicsuper(TopicAdminPermission,self).__init__()defrule(self):returnAdminRule()|TopicCreatorRule(self.topic)so we can useTopicAdminPermissioninedit_topicview:from.permissionsimportTopicAdminPermission@app.route('topic/<int:topic_id>/edit')defedit_topic(topic_id):topic=Topic.query.get_or_404(topic_id)permission=TopicAdminPermission(topic)ifnotpermission.check():returnpermission.deny()...LicenseMIT