AccessControl Security Framework for Zope
AccessControl is the foundational security framework for the Zope application server, providing core components for authentication, authorization, and permission management within Zope Object Database (ZODB) applications. It defines security policies, user management interfaces, and permission checking mechanisms. The current version is 7.3, and it follows the release cadence of the wider Zope ecosystem, with stable updates aligning with Zope 5+ development.
Common errors
-
ImportError: cannot import name 'User' from 'AccessControl.User'
cause Attempting to import a concrete user class directly from `AccessControl.User`. This module primarily defines interfaces and base classes, not specific user implementations for ZODB.fixFor standard Zope installations using ZODB, import user-related classes from `OFS.User` (e.g., `from OFS.User import UserFolder`) or refer to your specific user management product. If creating custom users, implement `AccessControl.interfaces.IUser`. -
Unauthorized: You are not authorized to access this resource. (or similar 'Insufficient privileges')
cause The current authenticated user (or anonymous user) in the security context lacks the necessary permission to perform the requested action on the target object.fix1. Verify the active user's roles (`getSecurityManager().getUser().getRoles()`). 2. Check the permissions assigned to those roles on the specific object or its parent containers through Zope's management interface. 3. Ensure a security manager is correctly set (`setSecurityManager()`) for the current request/thread. -
AttributeError: 'NoneType' object has no attribute 'checkPermission' (or similar errors when security manager is missing)
cause `getSecurityManager()` is returning `None` because no security manager has been established for the current thread or context. `AccessControl` relies on a thread-local security manager to be present.fixExplicitly set a security manager using `AccessControl.SecurityManagement.setSecurityManager(my_security_manager_instance)` before performing security-sensitive operations. In a Zope application, this is typically handled automatically by the Zope request lifecycle.
Warnings
- breaking AccessControl versions 7.x require Python 3.10 or newer. Users migrating from older Python 3 versions (e.g., 3.8, 3.9) or Python 2 will encounter incompatibility errors.
- gotcha AccessControl is designed specifically for Zope applications and is tightly coupled with the Zope Object Database (ZODB) and its request lifecycle. It is not a standalone, general-purpose security library (e.g., like Flask-Security or Django-Auth) and attempting to use it outside a proper Zope context will require extensive mocking or lead to unexpected behavior.
- gotcha Using `AccessControl.User.UnrestrictedUser` in production code can be a significant security risk. As its name implies, this user bypasses all permission checks, effectively granting full administrative access. It is primarily intended for testing, bootstrapping, or highly controlled administrative scripts.
Install
-
pip install accesscontrol
Imports
- setSecurityManager
from AccessControl.SecurityManagement import setSecurityManager
- getSecurityManager
from AccessControl.SecurityManagement import getSecurityManager
- UnrestrictedUser
from AccessControl.SecurityManagement import UnrestrictedUser
from AccessControl.User import UnrestrictedUser
- view
from AccessControl.Permissions import view
Quickstart
import os
from AccessControl.SecurityManagement import setSecurityManager, getSecurityManager
from AccessControl.User import UnrestrictedUser
from AccessControl.Permissions import view as ViewPermission
# 1. Define a mock Security Manager and User for demonstration
# In a real Zope app, these would be provided by the Zope environment.
class MockUser(UnrestrictedUser):
def __init__(self, id, password, roles, domains, permissions=None):
super().__init__(id, password, roles, domains)
self._permissions = permissions if permissions is not None else {}
def getRoles(self):
return self._roles
def has_permission(self, permission_name, context):
# Simplified check: Manager role allows everything, otherwise check specific permissions.
if 'Manager' in self.getRoles():
return True
return permission_name in self._permissions.get(context.__class__.__name__, [])
class MockSecurityManager:
def __init__(self, user):
self._user = user
def getUser(self):
return self._user
def checkPermission(self, permission_name, context_object):
# In a real Zope app, this involves complex role/permission mappings.
return self._user.has_permission(permission_name, context_object)
def validate(self, granted_roles, context_object, permission_name):
# Simplified for demonstration. Real method involves deep Zope security context.
return self._user.has_permission(permission_name, context_object)
# 2. Define a mock context object that can be secured
class Document:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"<Document: {self.name}>"
# 3. Create mock users with different roles and permissions
admin_user = MockUser('admin', 'pwd', ['Manager'], 'all', permissions={'Document': [ViewPermission, 'Edit']})
editor_user = MockUser('editor', 'pwd', ['Editor'], 'local', permissions={'Document': ['Edit']})
viewer_user = MockUser('viewer', 'pwd', ['Viewer'], 'local', permissions={'Document': [ViewPermission]})
# 4. Create a document to test permissions against
my_document = Document("Secret Report")
print("--- Admin User Context ---")
setSecurityManager(MockSecurityManager(admin_user))
sm = getSecurityManager()
current_user = sm.getUser()
print(f"Current User: {current_user.getUserName()} (Roles: {current_user.getRoles()})")
print(f"Can '{ViewPermission}' on {my_document}? {sm.checkPermission(ViewPermission, my_document)}")
print(f"Can 'Edit' on {my_document}? {sm.checkPermission('Edit', my_document)}")
print("\n--- Viewer User Context ---")
setSecurityManager(MockSecurityManager(viewer_user))
sm = getSecurityManager()
current_user = sm.getUser()
print(f"Current User: {current_user.getUserName()} (Roles: {current_user.getRoles()})")
print(f"Can '{ViewPermission}' on {my_document}? {sm.checkPermission(ViewPermission, my_document)}")
print(f"Can 'Edit' on {my_document}? {sm.checkPermission('Edit', my_document)}")
print("\n--- Editor User Context ---")
setSecurityManager(MockSecurityManager(editor_user))
sm = getSecurityManager()
current_user = sm.getUser()
print(f"Current User: {current_user.getUserName()} (Roles: {current_user.getRoles()})")
print(f"Can '{ViewPermission}' on {my_document}? {sm.checkPermission(ViewPermission, my_document)}")
print(f"Can 'Edit' on {my_document}? {sm.checkPermission('Edit', my_document)}")
# In a real Zope app, you might restore the default security manager or a different one
# setSecurityManager(None) # Clears the thread-local manager