[Zope-Checkins] CVS: Zope3/lib/python/Zope/Security - Checker.py:1.1.2.1 GetDescr.py:1.1.2.1 ISecurityProxyFactory.py:1.1.2.1 Proxy.py:1.1.2.10 RestrictedInterpreter.py:1.1.2.3
Jim Fulton
jim@zope.com
Thu, 18 Apr 2002 18:02:24 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/Security
In directory cvs.zope.org:/tmp/cvs-serv30502/lib/python/Zope/Security
Modified Files:
Tag: SecurityProxy-branch
Proxy.py RestrictedInterpreter.py
Added Files:
Tag: SecurityProxy-branch
Checker.py GetDescr.py ISecurityProxyFactory.py
Log Message:
Added SecurityProxy checker implementation and checker registry.
I'm going to refactor the way the checker permission database is
expressed and add some convenience factories.
=== Added File Zope3/lib/python/Zope/Security/Checker.py ===
from Zope.Security.IChecker import IChecker
from Zope.Exceptions import Unauthorized, Forbidden
from GetDescr import GetDescr
# XXX SecurityManagement needs to move out of App
from Zope.App.Security.SecurityManagement import getSecurityManager
class Checker:
__implements__ = IChecker
def __init__(self, database):
"""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.
"""
self.__database = database
############################################################
# Implementation methods for interface
# Zope.Security.IChecker.
def check_getattr(self, object, name):
'See Zope.Security.IChecker.IChecker'
checkDatabase(self.__database, name, object)
def check(self, object, name):
checkDatabase(self.__database, 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):
# 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)
raise Forbidden(name)
# Import this last due to module dependencies
from Proxy import Proxy
=== Added File Zope3/lib/python/Zope/Security/GetDescr.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.
#
##############################################################################
"""
Get a descriptor corresponding to an attribute.
Revision information:
$Id: GetDescr.py,v 1.1.2.1 2002/04/18 22:02:23 jim Exp $
"""
def GetDescr(obj, name):
"""
Get the descriptor for obj.<name>, if one exists.
Arguments:
obj: the object; must be a new-style instance
name: the attribute name; must be a string
Return a descriptor (something implementing __get__) that controls
(at least read) access to obj.<name>, or None if obj.<name> is not
controlled by a descriptor, or if obj.<name> does not exist.
Examples:
1. If the attribute is a property, it is definitely returned.
2. If the attribute is a method, it is returned unless it is
overridden in obj.__dict__.
3. If the attribute is a simple value (e.g. an int), either stored
in obj.__dict__ or as a class attribute, None is returned.
4. If the attribute doesn't exist, None is returned.
"""
if not isinstance(obj.__class__, type):
raise TypeError("obj must be a new-style instance")
if not isinstance(name, (str, unicode)):
raise TypeError("name must be a string")
try:
d = obj.__dict__
except AttributError:
isivar = 0
else:
isivar = name in d
for cls in obj.__class__.__mro__:
d = cls.__dict__
if name in d:
found = d[name]
break
else:
return None
if not hasattr(found, "__get__"):
return None
if not isivar or hasattr(found, "__set__"):
return found
return None
=== Added File Zope3/lib/python/Zope/Security/ISecurityProxyFactory.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: ISecurityProxyFactory.py,v 1.1.2.1 2002/04/18 22:02:23 jim Exp $
"""
from Interface import Interface
class ISecurityProxyFactory(Interface):
def __call__(object, checker=None):
"""Create a security proxy
If a checker is given, then use it, otherwise, try to figure
out a checker.
"""
=== Zope3/lib/python/Zope/Security/Proxy.py 1.1.2.9 => 1.1.2.10 ===
-from Zope.Security._Proxy import _Proxy
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
-def Proxy(obj, checker):
- if obj is None:
- return None
- elif isinstance(obj, int):
- return obj
- elif isinstance(obj, float):
- return obj
- elif isinstance(obj, str):
- return obj
- elif isinstance(obj, unicode):
- return obj
- elif isinstance(obj, _Proxy):
- return obj
- elif isinstance(obj, types.ClassType) and issubclass(obj, Exception):
- # XXX can't wrap exceptions because we can't raise proxies
- return obj
+$Id$
+"""
+
+from ISecurityProxyFactory import ISecurityProxyFactory
+from _Proxy import _Proxy
+from types import InstanceType, ClassType, FunctionType, MethodType, ModuleType
+from Zope.Exceptions import DuplicationError
+
+def Proxy(object, checker=None):
+ if checker is None:
+ checker = _checkers.get(type(object), _defaultChecker)
+ if checker is None:
+ return object
+ if not isinstance(checker, Checker):
+ checker = checker(object)
+ if checker is None:
+ return object
else:
- return _Proxy(obj, checker)
+ # Maybe someone passed us a proxy and a checker
+ if type(object) is _Proxy:
+ # XXX should we keep the existing proxy or create a new one.
+ return object
+
+ return _Proxy(object, checker)
+
+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(())
+
+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
=== Zope3/lib/python/Zope/Security/RestrictedInterpreter.py 1.1.2.2 => 1.1.2.3 ===
if k not in self.nok_builtin_names:
self.builtins[k] = Proxy(v, self.checker)
- self.builtins['__import__'] = Proxy(self.ri_import, self.checker)
+ self.builtins['__import__'] = Proxy(self.ri_import)
def ri_import(self, name, globals, locals, fromlist):
# XXX handle fromlist
return sys.modules[name]
-
-
-