Ferris uses the concept of authorization chains to control access to controllers and their actions. The concept is simple: an authorization chain consists of a series of functions (or callables). When trying to determine to allow or deny a request, each function in the chain is called. If any of the functions return False then the request is rejected. Chains are specified using Controller.Meta.authorizations and the add_authorizations() decorator.
Here’s an example of requiring a user to be logged in to access a controller:
from ferris import Controller def require_user(controller): return True if controller.user else False class Protected(Controller): class Meta: authorizations = (require_user,) ...
You can also use add_authorizations() instead:
@route @add_authorizations(require_user) def protected_action(self): ...
Adds additional authorization chains to a particular action. These are executed after the chains set in Controller.Meta.
To add authorizations globally, use the global event bus.
Creating Authorization Functions¶
As shown above, a simple authorization function is rather trivial:
def require_user(controller): return True if controller.user else False
Note that you can also include a message:
def require_user(controller): return True if controller.user else (False, "You must be logged in!")
Or if you’d like to redirect or present your own error page, you can return any valid response:
def require_user(controller): if controller.user: return True url = users.create_login_url(dest_url=controller.request.url) return controller.redirect(url)
Using Authorization Chains with Scaffolding¶
By default, scaffolded actions are not protected and are accessable to anyone with the instance key. Take care when exposing public facing actions to protect any sensitive data. Please use authorization chains where necessary and roll your own security model to protect any data that requires protection.
The example below wraps certain actions with a layer of protection that checks if the calling user has access to the requested data. The implementation of has_access is left up to the developer.
- def check_access_first(delegate):
secure_edit = check_access_first(scaffold.edit) secure_view = check_access_first(scaffold.view) secure_delete = check_access_first(scaffold.delete)
If the calling user has access to the data, the delegated function is called and Ferris proceeds as normal. Notice that the function returns 404 if the item doesn’t exist and 401 if the user does not have access as determined by has_access.
Built-in Authorization Functions¶
The module ferris.core.auth includes some built-in useful authorization functions and utilities.
Requires that a user is logged in
Requires that a user is logged in and that the user is and administrator on the App Engine Application
There are a few function generators that use predicates. These are useful shortcut authorization functions.
Generates an authorization function that requires that users are logged in for the given prefix.
Generates an authorization function that requires that the user is an App Engine admin for the given prefix.
- ferris.core.auth.require_user_for_action(*args, **kwargs)¶
- ferris.core.auth.require_admin_for_action(*args, **kwargs)¶
- ferris.core.auth.require_user_for_route(*args, **kwargs)¶
- ferris.core.auth.require_admin_for_route(*args, **kwargs)¶
You can also create your own generators using precidates.
Then use predicate_chain to combine them with your authorization function.
- ferris.core.auth.predicate_chain(predicate, chain)¶
Returns the result of chain if predicate returns True, otherwise returns True.