[Zope-Checkins] CVS: Zope3/lib/python/Zope/Security - CheckerRegistry.py:1.1.2.1 Checker.py:1.1.2.3 Proxy.py:1.1.2.11
Jim Fulton
jim@zope.com
Thu, 18 Apr 2002 20:29:10 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/Security
In directory cvs.zope.org:/tmp/cvs-serv16151/lib/python/Zope/Security
Modified Files:
Tag: SecurityProxy-branch
Checker.py Proxy.py
Added Files:
Tag: SecurityProxy-branch
CheckerRegistry.py
Log Message:
Refactored Checker constructor to simply take a function to compute a
permission from a name.
Provided some convenience checker factory functions.
Moved checker registry into a separate module.
=== Added File Zope3/lib/python/Zope/Security/CheckerRegistry.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: CheckerRegistry.py,v 1.1.2.1 2002/04/19 00:29:08 jim Exp $
"""
from ISecurityProxyFactory import ISecurityProxyFactory
from _Proxy import _Proxy
from types import InstanceType, ClassType, FunctionType, MethodType, ModuleType
from Zope.Exceptions import DuplicationError
from Checker import Checker, NamesChecker
NoProxy = object()
# _checkers is a mapping.
#
# - Keys are types
#
# - Values are
#
# o None => rock
# o a Checker
# o a function returning None or a Checker
#
_checkers = {}
getChecker = _checkers.get
_defaultChecker = Checker({}.get)
def _instanceChecker(inst):
checker = _checkers.get(inst.__class__, _defaultChecker)
if checker is _defaultChecker and isinstance(inst, Exception):
return NoProxy # XXX we should be more careful
return checker
def _classChecker(class_):
checker = _checkers.get(class_, _typeChecker)
if checker is _typeChecker and issubclass(class_, Exception):
return NoProxy # XXX we should be more careful
return checker
_callableChecker = NamesChecker(['__str__', '__repr__', '__hash__',
'__call__'])
_typeChecker = NamesChecker(['__str__', '__repr__', '__hash__'])
def _moduleChecker(module):
return _checkers.get(module, _typeChecker)
_default_checkers = {
dict: NamesChecker(['__getitem__', 'get', 'has_key', '__len__',
'keys', 'values', 'items']),
list: NamesChecker(['__getitem__', 'index', 'count', '__len__']),
# YAGNI: () a rock
tuple: NamesChecker(['__getitem__', '__len__']),
int: NoProxy,
float: NoProxy,
long: NoProxy,
complex: NoProxy,
type(None): NoProxy,
str: NoProxy, # Woo hoo
unicode: NoProxy,
InstanceType: _instanceChecker,
_Proxy: NoProxy,
ClassType: _classChecker,
FunctionType: _callableChecker,
MethodType: _callableChecker,
type: _typeChecker,
ModuleType: _moduleChecker,
# XXX bool
}
def _clear():
_checkers.clear()
_checkers.update(_default_checkers)
from Zope.Testing.CleanUp import addCleanUp
addCleanUp(_clear)
def defineChecker(type_, checker):
if type_ in _checkers:
raise DuplicationError(type_)
_checkers[type_] = checker
=== Zope3/lib/python/Zope/Security/Checker.py 1.1.2.2 => 1.1.2.3 ===
-from Zope.Exceptions import Unauthorized, Forbidden
+from Zope.Exceptions import Unauthorized, Forbidden, DuplicationError
# XXX SecurityManagement needs to move out of App
from Zope.App.Security.SecurityManagement import getSecurityManager
+from Interface.IInterface import IInterface
+
+# Marker for public attributes
+CheckerPublic = object()
class Checker:
__implements__ = IChecker
- def __init__(self, database):
+ def __init__(self, permission_func):
"""Create a checker
- An optional database may be provided. If it is provides, then
- it is a sequence of name-tester and permission pairs. A name
- tester is a callable object that takes a name and returns a
- boolean indicating whether the name matches.
+ A callable must be provided for computing permissions for
+ names. The callable will be called with attribute names and
+ must return a permission id, None, or the special marker,
+ CheckerPublic. If None is returned, then access to the name is
+ forbidden. If CheckerPublic is returned, then access will be
+ granted without checking a permission.
"""
- self.__database = database
-
+ self.__permission_func = permission_func
+
+
+ def permission_id(self, name):
+ """Return the result of calling the permission func
+ """
+ return self.__permission_func(name)
############################################################
# Implementation methods for interface
@@ -26,33 +37,100 @@
def check_getattr(self, object, name):
'See Zope.Security.IChecker.IChecker'
- checkDatabase(self.__database, name, object)
+ check(self.__permission_func, name, object)
- def check(self, object, name):
- checkDatabase(self.__database, name, object)
+ def check(self, object, name):
+ check(self.__permission_func, name, object)
def proxy(self, value):
'See Zope.Security.IChecker.IChecker'
# Now we need to create a proxy
return Proxy(value)
-
#
############################################################
-def checkDatabase(database, name, object):
+
+def check(permission_func, name, object):
# We have the information we need already
- for check, permission in database:
- if check(name):
- if permission is None:
- return
- manager = getSecurityManager()
- if manager.checkPermission(permission, object):
- return
- else:
- raise Unauthorized(name=name)
+ permission = permission_func(name)
+ if permission:
+ if permission is CheckerPublic:
+ return # Public
+ manager = getSecurityManager()
+ if manager.checkPermission(permission, object):
+ return
+ else:
+ raise Unauthorized(name=name)
raise Forbidden(name)
+
+def NamesChecker(names, permission_id=CheckerPublic, **__kw__):
+ """Return a checker that grants access to a set of names.
+
+ A sequence of names is given as the first argument. If a second
+ argument, permission_id, is given, it is the permission required
+ to access the names. Additional names and persmission ids can be
+ supplied as keyword arguments.
+ """
+
+ data = {}
+ data.update(__kw__)
+ for name in names:
+ if data.get(name, permission_id) is not permission_id:
+ raise DuplicationError(name)
+ data[name] = permission_id
+
+ return Checker(data.get)
+
+def MultiChecker(specs):
+ """Create a checker from a sequence of specifications
+
+ A specification is:
+
+ - A two-tuple with:
+
+ o a sequence of names or an interface
+
+ o a permission id
+
+ All the names in the sequence of names or the interface are
+ protected by the permission.
+
+ - A dictionoid (having anitems method), with items that are
+ name/permission-id pairs.
+ """
+ data = {}
+
+ for spec in specs:
+ if type(spec) is tuple:
+ names, permission_id = spec
+ if IInterface.isImplementedBy(names):
+ names = names.names(1)
+ for name in names:
+ if data.get(name, permission_id) is not permission_id:
+ raise DuplicationError(name)
+ data[name] = permission_id
+ else:
+ for name, permission_id in spec.items():
+ if data.get(name, permission_id) is not permission_id:
+ raise DuplicationError(name)
+ data[name] = permission_id
+
+ return Checker(data.get)
+
+def NonPrivateChecker(permission_id = CheckerPublic):
+
+ def check(name, permission_id=permission_id):
+ if name.startswith('_'):
+ return None
+ return permission_id
+
+ return Checker(check)
+
# Import this last due to module dependencies
from Proxy import Proxy
+
+
+
=== Zope3/lib/python/Zope/Security/Proxy.py 1.1.2.10 => 1.1.2.11 ===
def Proxy(object, checker=None):
- if checker is None:
- checker = _checkers.get(type(object), _defaultChecker)
- if checker is None:
+ if checker is None:
+ checker = getChecker(type(object), _defaultChecker)
+ if checker is NoProxy:
return object
+
if not isinstance(checker, Checker):
checker = checker(object)
- if checker is None:
+ if checker is NoProxy:
return object
+
else:
# Maybe someone passed us a proxy and a checker
if type(object) is _Proxy:
@@ -40,83 +42,7 @@
Proxy.__implements__ = ISecurityProxyFactory
-def namesChecker(names, permission=None):
- d = {}
- for name in names:
- d[name] = 1
- return Checker([(d.has_key, permission)])
-
-
-# Ugh.
-#
-# _checkers is a mapping.
-#
-# - Keys are types
-#
-# - Values are
-#
-# o None => rock
-# o a Checker
-# o a function returning None or a Checker
-#
-
from Checker import Checker
+_defaultChecker = Checker({}.get)
-_defaultChecker = Checker(())
-
-def _instanceChecker(inst):
- if isinstance(inst, Exception):
- return None # XXX we should be more careful
-
- return _checkers.get(inst.__class__, _defaultChecker)
-
-def _classChecker(class_):
- if issubclass(class_, Exception):
- return None # XXX we should be more careful
-
- return _typeChecker
-
-_callableChecker = namesChecker(['__str__', '__repr__', '__hash__',
- '__call__'])
-_typeChecker = namesChecker(['__str__', '__repr__', '__hash__'])
-
-def _moduleChecker(module):
- return _checkers.get(module, _typeChecker)
-
-
-_default_checkers = {
- dict: namesChecker(['__getitem__', 'get', 'has_key', '__len__',
- 'keys', 'values', 'items']),
- list: namesChecker(['__getitem__', 'index', 'count', '__len__']),
- # YAGNI: () a rock
- tuple: namesChecker(['__getitem__', '__len__']),
- int: None,
- float: None,
- long: None,
- complex: None,
- type(None): None,
- str: None, # Woo hoo
- unicode: None,
- InstanceType: _instanceChecker,
- _Proxy: None,
- ClassType: _classChecker,
- FunctionType: _callableChecker,
- MethodType: _callableChecker,
- type: _typeChecker,
- ModuleType: _moduleChecker,
- # XXX bool
- }
-
-
-_checkers = {}
-def _clear():
- _checkers.clear()
- _checkers.update(_default_checkers)
-
-from Zope.Testing.CleanUp import addCleanUp
-addCleanUp(_clear)
-
-def defineChecker(type_, checker):
- if type_ in _checkers:
- raise DuplicationError(type_)
- _checkers[type_] = checker
+from CheckerRegistry import getChecker, NoProxy