Permissions Tutorial
Author: | Luis De la Parra; Uli Fouquet; Jan-Wijbrand Kolman |
---|
Intended Audience:
- Python Developers
- Zope 2 Developers
- Zope 3 Developers
Introduction
Zope3 and Grok come with authorization capabilities out of the box. While a vanilla Zope3 application protects all content by default and performs authorization checks on the content objects themselves, Grok allows access to everything unless you explicitly restrict it. The authorization checks here are done based on the Views used to access (display/manipulate) the content.
Setup
#contact.py from zope import component, interface, schema import grok class IContactInfo(interface.Interface): """ Interface/Schema for Contact Information """ first_name = schema.Text(title=u'First Name') last_name = schema.Text(title=u'Last Name') email = schema.Text(title=u'E-mail') class ContactInfo(grok.Model): interface.implements(IContactInfo) first_name = '' last_name = '' email = '' class ViewContact(grok.View) """Display Contact Info, without e-mail. Anyone can use this view, even unauthenticated users over the internet """ def render(self): contact = self.context return 'Contact: ' + contact.first_name + contact.last_name
Defining Permissions and restricting access
As all Views in Grok default to public access, anyone can use the ViewContact-view defined above. If you want to restrict access to a view, you have to explicitly protect it with a permission:
# Define Permissions. The grok.name can be any string, but it is strongly # recommended to make them unique by prefixing them with the application # name. class ViewContacts(grok.Permission): grok.name('mysite.ViewContacts') grok.title('View Contacts') # optional class AddContacts(grok.Permission): grok.name('mysite.AddContacts') class EditContacts(grok.Permission): grok.name('mysite.EditContacts') class ViewContactComplete(grok.View) """Display Contact Info, including email. Only users which have the permission 'mysite.ViewContacts' can use this view. """" grok.require('mysite.ViewContacts') #this is the security declaration def render(self): contact = self.context return 'Contact: %s%s%s' % (contact.first_name, contact.last_name, contact.email)
Note The grok.Permission component base class was introduced after the release 0.10. In earlier versions of Grok a permission was defined using a module level directive, like so:
grok.define_permission('mysite.ViewContacts')
If you are using grokproject this change currently does not affect your installation. In this case use grok.define_permission as described above.
Granting Permissions
You can grant permissions to principals with a PermissionManager. For example, if all registered users should have permission to view contact details and to create new contacts, you could grant them the permissions when the user account is created:
from zope.app.security.interfaces import IAuthentication from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager def addUser(username, password, realname): """Create a new user. create a new user and give it the authorizations, ``ViewContacts`` and ``EditContacts``. This example assumes you are using a Pluggable Authentication Utility (PAU) / PrincipalFolder, which you have to create and register when creating your Application. """ pau = component.getUtility(IAuthentication) principals = pau['principals'] principals[username] = InternalPrincipal(username, password, realname) # grant the user permission to view and create contacts # everywhere in the site permission_man = IPrincipalPermissionManager(grok.getSite()) # NOTE that you need a principal ID. If you are # authenticating users with a PAU this is normally the user # name prepended with the principals-folder prefix (and the # PAU-prefix as well, if set) permission_man.grantPermissionToPrincipal ( 'mysite.ViewContacts', principals.prefix + username) permission_man.grantPermissionToPrincipal( 'mysite.AddContacts', principals.prefix + username)
Permissions are granted for the context for which the PermissionManager is created, and -- if not explicitly overridden -- all its children. The above example grants View and Add permissions for the complete site, unless a folder down in the hierarchy revokes the permission.
If you want users to be able to edit only their own ContactInfos, you have to give them the Edit permission only within the context of the ContactInfo-object itself
class AddContact(grok.AddForm): """Add a contact. """ # Only users with permission 'mysite.AddContacts' can use # this. # # NOTE that if you don't protect this Form, anyone -- even # anonymous/unauthenticated users -- could add ``Contacts`` # to the site. grok.require('mysite.AddContacts') #automagically generate form fields form_fields = grok.AutoFields(IContactInfo) @grok.action('Create') def create(self, **kw): # Create and add the ``ContactInfo`` to our context # (normally a folder/container) contact = ContactInfo() self.applyData(contact, **kw) self.context[contact.first_name] = contact # Grant the current user the Edit permission, but only in # the context of the newly created object. permission_man = IPrincipalPermissionManager(contact) permission_man.grantPermissionToPrincipal( 'mysite.EditContacts', self.request.principal.id) self.redirect(self.url(contact)) class EditContact(grok.EditForm): """Edit a contact. """ #only users with permission 'mysite.EditContacts' can use this grok.require('mysite.EditContacts') form_fields = grok.AutoFields(IContactInfo) @grok.action('Save Changes') def edit(self, **data): self.applyData(self.context, **data) self.redirect(self.url(self.context))
Checking Permissions
[FIXME How to check permissions in a page template and from python code? User Interfaces should not contain any links/actions which users cannot access / for which users don't have authorizations]
Defining Roles
Permissions can be grouped together in Roles, which makes granting all the permissions for a particular type of user much easier. Defining roles is similar to defining permissions.
As an example, let's group all permissions in two roles: one for normal site members, and one for administrators:
class MemberRole(grok.Role): grok.name('mysite.Member') grok.title('Contacts Member') # optional grok.permissions( 'mysite.ViewContacts', 'mysite.AddContacts') class AdministratorRole(grok.Role): grok.name('mysite.Editor') grok.title('Contacts Administrator') # optional grok.permissions( 'mysite.ViewContacts', 'mysite.AddContacts', 'mysite.EditContacts')
Now, if the context here is the site/application, users with the administrator role can edit all ContactInfos, regardless of who the creator is.
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager role_man = IPrincipalRoleManager(context) role_man.assignRoleToPrincipal('mysite.Administrator', principalID)