This scenario is implemented in access1.py:
Note: In the code distributed with this chapter and the following one, the logon
class is not only initialized with an admin
user (with admin
as a password) but with the following three name/password combinations: eve/eve, john/john
, and mike/mike
.
If your run this application and point your browser to http://localhost:8080
, you are presented with a list of accounts. If you have logged in as either john or mike both sales persons you can only alter the accounts owned by each of them. If however, you log in as eve, the sales manager, you can alter the information in all accounts.
The application is simple enough and follows a familiar pattern. The relevant definitions are shown here:
Chapter9/access1.py
from permissions1 import isallowed class Entity(AbstractEntity): database = db def update(self,**kw): if isallowed('update', self, logon.checkauth(), self.getUser()): super().update(**kw) class Relation(AbstractRelation): database = db class User(Entity): name = Attribute(notnull=True, unique=True, displayname="Name", primary=True) class Account(Entity): name = Attribute(notnull=True, displayname="Name", primary=True) class OwnerShip(Relation): a = User b = Account class AccountBrowser(Browse): edit = Display(Account, edit=True, logon=logon, columns=Account.columns+[User]) add = Display(Account, add=True, logon=logon, columns=Account.columns+[User]) class UserBrowser(Browse): edit = Display(User, edit=True, logon=logon) add = Display(User, add=True, logon=logon)
The database distributed with the code (access1.db) contains a number of accounts already so the code does not contain any lines to create those. The important part is highlighted in the preceding code: it imports a permissions1
module that contains a dictionary of permissions. This dictionary lists for each combination of entity, action, ownership, and username whether this is permissible or not.
We can now override the update()
method in the AbstractEntity
class (highlighted): We retrieve the username from the current sessions by calling the checkauth()
method and pass it along to the isallowed()
function, together with the name of the action we want to check (update in this case), the entity, and a list of users (the owners). If this checks out okay, we call the original update()
method.
If we take a look at permissions1.py
, we see that because, in this example, we only consider the Account
entity and the update action in this list is quite small:
Chapter9/permissions1.py
import entity1 allowed = { 'Account' : { 'create' : { 'admin' : 'all', 'eve' : 'all', 'john' : 'owner', 'mike' : 'owner' }, 'update' : { 'admin' : 'all', 'eve' : 'all', 'john' : 'owner', 'mike' : 'owner' }, 'delete' : { 'admin' : 'all', 'eve' : 'all', } } } def isallowed(action,entity,user,owner): if len(owner) < 1 : return True try: privileges = allowed[entity.__class__.__name__][action] if not user in privileges : return False elif privileges[user] == 'all': return True elif privileges[user] == 'owner' and user == owner[0].name: return True else: return False except KeyError: return True
The dictionary with privileges itself is called allowed
(highlighted) and permissions1.py
also defines a function called isallowed()
, that will return True
if there aren't any owners for this entity. Then it checks if there are any privileges known for this entity and action. If this is not the case, any exception will be raised because either the key for the entity or the key for the action does not exist.
If there are privileges known, we check if the user has specific privileges. If there is no key for the user, we return False
. If there is, and the privilege is all
, we return True:
he/she may perform the action on this entity even for an entity instance he/she doesn't own. If the privilege is the owner, we only return True
if the user is in fact the owner.
The aforementioned approach outlined is cumbersome for various reasons:
Account
entity and the update
action, but in a somewhat more realistic application, there would be tens of entities (like Contact, Address, Quote, Lead
, and so on) and quite a few actions more to consider (for example, delete
and create
, but also actions that involve other entities like changing ownership or adding an address to an account). Also, if that sales person was promoted to sales manager, we would have to repeat the whole exercise again.The last reason is why we will implement the list of permissions in the database. After all, we already have a framework that allows for easy manipulation of database entries with a web interface. The other reasons are why we will reconsider our first approach and will implement a scheme called role-based access control.
3.139.67.5