PyCan is a framework-agnostic open source authorization library that can be easily integrated with any Python application.
PyCan's primary goal is to make sure users are kept on the line and don't do anything they were not supposed to.
PyCan uses a white-list approach, which means that authorization will be denied for any (action, target) combinations that were not explicitly granted permissions to with a call to
pip install pycan
1. Declare authorization rules
from pycan import can can(['delete', 'edit'], 'profile', lambda user, context, profile: user.id in profile.owners, load_before=lambda user, context: load_profile(context.profile_id))
As the example above shows, a call to
can always expects an
action (the first parameter), a
target (the second parameter) and an
authorization rule (the 3rd parameter).
load_before parameter is optional and it will be explained further below.
1.1. Actions and Targets
target pair is basically what a given
user is trying to do with the application. In the example above, a user is trying to edit or delete (actions) a profile (target).
action and the
target parameters accept lists. For such cases, the authorization rule will be applied to any combination of actions and targets.
1.2. Authorization rules
Authorization rules are functions that determine whether or not the user is allowed to perform the
action on the
target. Those functions must always accept three parameters: user, context and authorization resource.
This is obvious :)
The context is everything that is going on at the moment the authorization took place. In a web application the context usually represents the request parameters.
The authorization resource is any data that was loaded via the lambda passed to the
load_before parameter (see section below).
load_before parameter is responsible for loading data required to perform the authorization.
In the example above, we are trying to make sure a user can edit or delete a profile. In order to determine that, the profile must be loaded prior to running the authorization rule. That's where the
load_before parameter comes to picture. This parameter accepts a function that will be responsible for loading the profile (the authorization resource). Its return value is passed as the 3rd parameter of the authorization rule (the
This parameter is optional and it is called only if authorization succeeds. Its main goal is to provide the application with the resource it needs to implement the user action.
2. Combining authorization rules
In section 1 we have taught you how to create a basic authorization rule. Now, say you would like to allow admin users to delete/edit anybody's profiles. One way to implement this is to change the authorization rule to also check if the user is an admin:
from pycan import can can(['delete', 'edit'], 'profile', lambda user, context, profile: user.admin or (user.id in profile.owners), load_before=lambda user, context: load_profile(context.profile_id))
The problem with the implementation above is that it does not favor code reuse. The
user.admin check would need to be duplicated on every place where admins have special permissions.
Fortunately, PyCan allows you to effortlessly combine authorization functions through the
from pycan import can, or_ def user_is_admin(user, context, resource): return user.admin can(['delete', 'edit'], 'profile', or_( user_is_admin, lambda user, context, profile: user.id in profile.owners), load_before=lambda user, context: load_profile(context.profile_id))
or_ functions also do perform short-circuiting. Therefore, it will not make unnecessary evaluations of authorization rules. In the example above, if the user is an admin, the second authorization rule would not have been executed.
The authorization itself happens when your application's entry points are intercepted with the
from pycan import authorize try: pycan.authorize(action, target, user=user, context=context) except UnauthorizedResourceError, e: abort(403)
context are mapped to the exact same objects used in the calls to
The call to
authorize will raise an UnauthorizedResourceError if the authorization rule defined by the call to
can does not evaluate to True or if an authorization rule for that given action/target pair does not exist.
It is up to your program to handle that exception. For instance, in a web app, you could have the app return a 403.