Commit d2b68599 authored by Hanno Schlichting's avatar Hanno Schlichting

Use the external AccessControl distribution

parent ccc10355
......@@ -36,6 +36,7 @@ scripts = zopepy
recipe = zc.recipe.testrunner
eggs =
Zope2
AccessControl
Acquisition
DateTime
ExtensionClass
......
......@@ -31,15 +31,6 @@ setup(name='Zope2',
package_dir={'': 'src'},
ext_modules=[
# AccessControl
Extension(
name='AccessControl.cAccessControl',
include_dirs=['include', 'src'],
sources=['src/AccessControl/cAccessControl.c'],
depends=['include/ExtensionClass/ExtensionClass.h',
'include/Acquisition/Acquisition.h']),
# DocumentTemplate
Extension(
name='DocumentTemplate.cDocumentTemplate',
......@@ -58,6 +49,7 @@ setup(name='Zope2',
],
install_requires=[
'AccessControl',
'Acquisition',
'DateTime',
'ExtensionClass',
......
Security Architecture
---------------------
Users
-----
Objects representing users may be created in Principia
User Folder objects. User objects maintain the information
used to authenticate users, and allow roles to be associated
with a user.
Permissions
-----------
A "permission" is the smallest unit of access to an object,
roughly equivalent to the atomic permissions seen in NT:
R (Read), W(Write), X(Execute), etc. In Principia, a permission
usually describes a fine-grained logical operation on an object,
such as "View Management Screens", "Add Properties", etc.
Different types of objects will define different permissions
as appropriate for the object.
Types of access
---------------
A "type of access" is a named grouping of 0 or more of the
permissions defined by an object. All objects have one predefined
type of access called Full Access (all permissions defined by that
object). A user who has the special role "Manager" always has Full
Access to all objects at or below the level in the object hierarchy
at which the user is defined.
New types of access may be defined as combinations of the
various permissions defined by a given object. These new
types of access may be defined by the programmer, or by
users at runtime.
Roles
-----
A role is a name that ties users (authentication of identity)
to permissions (authorization for that identity) in the system.
Roles may be defined in any Folder (or Folderish) object in the
system. Sub folders can make use of roles defined higher in the
hierarchy. These roles can be assigned to users. All users,
including non-authenticated users have the built-in role of
"Anonymous".
Principia objects allow the association of defined roles
with a single "type of access" each, in the context of that
object. A single role is associated with one and only one
type of access in the context of a given object.
Examples
--------
User Object1
o has the role "RoleA" o has given "RoleA" Full Access
Result: the user has Full Access to Object1.
User Object2
o has the role "RoleA" o has given "RoleB" Full Access
o has given the role "RoleA" View Access,
a custom type of access that allows only
viewing of the object.
Result: the user has only View Access.
Notes
-----
All objects define a permission called "Default permission". If this
permission is given to a role, users with that role will be able to
access subobjects which do not explicitly restrict access.
Technical
---------
Objects define their permissions as logical operations.
Programmers have to determine the appropriate operations
for their object type, and provide a mapping of permission
name to attribute names. It is important to note that permissions
cannot overlap - none of the attributes named in a permission
can occur in any of the other permissions. The following are
proposed permissions for some current principia objects:
Folder
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
Confera Topic
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
o Change Configuration
o Add Messages
o Change Messages
o Delete Messages
Tabula Collection
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
o Change schema
o Upload data
o Add computed fields
o Change computed fields
o Delete computed fields
Document/Image/File
o View management screens
o Change permissions
o Change/upload data
o View
Session
o View management screens
o Change permissions
o Change session config
o Join/leave session
o Save/discard session
Mail Host
o View management screens
o Change permissions
o Change configuration
To support the architecture, developers must derive an
object from the AccessControl.rolemanager.RoleManager mixin class,
and define in their class an __ac_permissions__ attribute.
This should be a tuple of tuples, where each tuple represents
a permission and contains a string permission name as its first
element and a list of attribute names as its second element.
Example:
__ac_permissions__=(
('View management screens',
['manage','manage_menu','manage_main','manage_copyright',
'manage_tabs','manage_propertiesForm','manage_UndoForm']),
('Undo changes', ['manage_undo_transactions']),
('Change permissions', ['manage_access']),
('Add objects', ['manage_addObject']),
('Delete objects', ['manage_delObjects']),
('Add properties', ['manage_addProperty']),
('Change properties', ['manage_editProperties']),
('Delete properties', ['manage_delProperties']),
('Default permission', ['']),
)
The developer may also predefine useful types of access, by
specifying an __ac_types__ attribute. This should be a tuple of
tuples, where each tuple represents a type of access and contains
a string name as its first element and a list of permission names
as its second element.
By default, only "Full Access" is defined (by the RoleManager mixin).
If you wish to override __ac_types__ to provide convenient types of
access, you must always be sure to define "Full Access" as containing
the names of all possible permissions for your object.
Example:
__ac_types__=(
('Full Access', map(lambda x: x[0], __ac_permissions__)),
('Change', ['Add Objects', 'Add Properties', 'Change Properties']),
)
Developers may also provide pre-defined role names that are
not deletable via the interface by specifying an __ac_roles__
attribute. This is probably not something we'll ever use under
the new architecture, but it's there if you need it.
Example:
__ac_roles__=('Manager', 'Anonymous')
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
__version__='$Revision: 1.9 $'[11:-2]
try:
from hashlib import sha1 as sha
except:
from sha import new as sha
import binascii
from binascii import b2a_base64, a2b_base64
from random import choice, randrange
class PasswordEncryptionScheme: # An Interface
def encrypt(pw):
"""
Encrypt the provided plain text password.
"""
def validate(reference, attempt):
"""
Validate the provided password string. Reference is the
correct password, which may be encrypted; attempt is clear text
password attempt.
"""
_schemes = []
def registerScheme(id, s):
'''
Registers an LDAP password encoding scheme.
'''
_schemes.append((id, '{%s}' % id, s))
def listSchemes():
r = []
for id, prefix, scheme in _schemes:
r.append(id)
return r
class SSHADigestScheme:
'''
SSHA is a modification of the SHA digest scheme with a salt
starting at byte 20 of the base64-encoded string.
'''
# Source: http://developer.netscape.com/docs/technote/ldap/pass_sha.html
def generate_salt(self):
# Salt can be any length, but not more than about 37 characters
# because of limitations of the binascii module.
# 7 is what Netscape's example used and should be enough.
# All 256 characters are available.
salt = ''
for n in range(7):
salt += chr(randrange(256))
return salt
def encrypt(self, pw):
pw = str(pw)
salt = self.generate_salt()
return b2a_base64(sha(pw + salt).digest() + salt)[:-1]
def validate(self, reference, attempt):
try:
ref = a2b_base64(reference)
except binascii.Error:
# Not valid base64.
return 0
salt = ref[20:]
compare = b2a_base64(sha(attempt + salt).digest() + salt)[:-1]
return (compare == reference)
registerScheme('SSHA', SSHADigestScheme())
class SHADigestScheme:
def encrypt(self, pw):
return b2a_base64(sha(pw).digest())[:-1]
def validate(self, reference, attempt):
compare = b2a_base64(sha(attempt).digest())[:-1]
return (compare == reference)
registerScheme('SHA', SHADigestScheme())
# Bogosity on various platforms due to ITAR restrictions
try:
from crypt import crypt
except ImportError:
crypt = None
if crypt is not None:
class CryptDigestScheme:
def generate_salt(self):
choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789./")
return choice(choices) + choice(choices)
def encrypt(self, pw):
return crypt(pw, self.generate_salt())
def validate(self, reference, attempt):
a = crypt(attempt, reference[:2])
return (a == reference)
registerScheme('CRYPT', CryptDigestScheme())
class MySQLDigestScheme:
def encrypt(self, pw):
nr = 1345345333L
add = 7
nr2 = 0x12345671L
for i in pw:
if i == ' ' or i == '\t':
continue
nr ^= (((nr & 63) + add) * ord(i)) + (nr << 8)
nr2 += (nr2 << 8) ^ nr
add += ord(i)
r0 = nr & ((1L << 31) - 1L)
r1 = nr2 & ((1L << 31) - 1L)
return "%08lx%08lx" % (r0, r1)
def validate(self, reference, attempt):
a = self.encrypt(attempt)
return (a == reference)
registerScheme('MYSQL', MySQLDigestScheme())
def pw_validate(reference, attempt):
"""Validate the provided password string, which uses LDAP-style encoding
notation. Reference is the correct password, attempt is clear text
password attempt."""
for id, prefix, scheme in _schemes:
lp = len(prefix)
if reference[:lp] == prefix:
return scheme.validate(reference[lp:], attempt)
# Assume cleartext.
return (reference == attempt)
def is_encrypted(pw):
for id, prefix, scheme in _schemes:
lp = len(prefix)
if pw[:lp] == prefix:
return 1
return 0
def pw_encrypt(pw, encoding='SSHA'):
"""Encrypt the provided plain text password using the encoding if provided
and return it in an LDAP-style representation."""
for id, prefix, scheme in _schemes:
if encoding == id:
return prefix + scheme.encrypt(pw)
raise ValueError, 'Not supported: %s' % encoding
pw_encode = pw_encrypt # backward compatibility
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Add security system support to Document Templates
"""
from zope.deferredimport import deprecated
deprecated("Please import from DocumentTemplate.security",
DTMLSecurityAPI = 'DocumentTemplate.security:DTMLSecurityAPI',
RestrictedDTML = 'DocumentTemplate.security:RestrictedDTML',
)
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""C implementation of the access control machinery."""
try:
from AccessControl.cAccessControl import rolesForPermissionOn
from AccessControl.cAccessControl import PermissionRole
from AccessControl.cAccessControl import imPermissionRole
from AccessControl.cAccessControl import _what_not_even_god_should_do
from AccessControl.cAccessControl import aq_validate
from AccessControl.cAccessControl import guarded_getattr
from AccessControl.cAccessControl import setDefaultBehaviors
from AccessControl.cAccessControl import ZopeSecurityPolicy \
as cZopeSecurityPolicy
from AccessControl.cAccessControl import SecurityManager \
as cSecurityManager
except ImportError:
import sys
# make sure a partial import doesn't pollute sys.modules
del sys.modules[__name__]
raise
from AccessControl.ImplPython import SecurityManager
from AccessControl.ImplPython import ZopeSecurityPolicy
class ZopeSecurityPolicy(cZopeSecurityPolicy, ZopeSecurityPolicy):
"""A security manager provides methods for checking access and managing
executable context and policies
"""
class SecurityManager(cSecurityManager, SecurityManager):
"""A security manager provides methods for checking access and managing
executable context and policies
"""
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Controller that can switch between security machinery implementations.
This module allows configuration of the security implementation after
the initial import of the modules. It is intended to allow runtime
selection of the machinery based on Zope's configuration file.
The helper function defined here switches between the 'C' and 'PYTHON'
security implementations by loading the appropriate implementation
module and wiring the implementation into the other modules of the
AccessControl package that defined the various components before this
module was introduced.
"""
def getImplementationName():
"""Return the name of the implementation currently being used."""
return _implementation_name
def setImplementation(name):
"""Select the policy implementation to use. The 'name' must be either
'PYTHON' or 'C'. NOTE: this function is intended to be called
exactly once, so that the Zope config file can dictate the policy
implementation to be used. Subsequent calls to this function will
have no effect!!
"""
import sys
global _implementation_name
global _implementation_set
if _implementation_set:
return
name = name.upper()
if name == _implementation_name:
return
if name == "C":
from AccessControl import ImplC as impl
elif name == "PYTHON":
from AccessControl import ImplPython as impl
else:
raise ValueError("unknown policy implementation: %r" % name)
_implementation_name = name
for modname, names in _policy_names.items():
__import__(modname)
mod = sys.modules[modname]
for n in names:
setattr(mod, n, getattr(impl, n))
if hasattr(mod, "initialize"):
mod.initialize(impl)
from AccessControl.SecurityManager import setSecurityPolicy
policy = impl.ZopeSecurityPolicy(True, True)
setSecurityPolicy(policy)
_implementation_set = 1
_implementation_name = None
_implementation_set = 0
_policy_names = {
"AccessControl": ("setDefaultBehaviors",
),
"AccessControl.PermissionRole": ("_what_not_even_god_should_do",
"rolesForPermissionOn",
"PermissionRole",
"imPermissionRole",
),
"AccessControl.SecurityManagement": ("SecurityManager",
),
"AccessControl.SecurityManager": ("SecurityManager",
),
"AccessControl.ZopeGuards": ("aq_validate",
"guarded_getattr",
),
"AccessControl.ZopeSecurityPolicy": ("ZopeSecurityPolicy",
),
}
# start with the default, mostly because we need something for the tests
setImplementation("C")
# allow the implementation to change from the default
_implementation_set = 0
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Support for owned objects
"""
# BBB
from .owner import absattr
from .owner import EditUnowned
from .owner import EmergencyUserCannotOwn
from .owner import ownableFilter
from .owner import ownerInfo
from .owner import UnownableOwner
from zope.deferredimport import deprecated
deprecated("Owned is no longer part of AccessControl, please "
"depend on Zope2 and import from OFS.owner or use the "
"new minimal Owned class from AccessControl.owner.",
Owned = 'OFS.owner:Owned',
)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Permissions
"""
import string
from Acquisition import aq_base
name_trans=filter(lambda c, an=string.letters+string.digits+'_': c not in an,
map(chr, range(256)))
name_trans=string.maketrans(''.join(name_trans), '_'*len(name_trans))
def pname(name, translate=string.translate, name_trans=name_trans):
return '_'+translate(name, name_trans) + "_Permission"
_marker=[]
class Permission:
# A Permission maps a named logical permission to a set
# of attribute names. Attribute names which appear in a
# permission may not appear in any other permission defined
# by the object.
def __init__(self, name, data, obj, default=None):
self.name = name
self._p = '_' + string.translate(name, name_trans) + "_Permission"
self.data = data
self.obj = aq_base(obj)
self.default = default
def getRoles(self, default=_marker):
# Return the list of role names which have been given
# this permission for the object in question. To do
# this, we try to get __roles__ from all of the object
# attributes that this permission represents.
obj = self.obj
name = self._p
if hasattr(obj, name):
return getattr(obj, name)
roles = default
for name in self.data:
if name:
if hasattr(obj, name):
attr = getattr(obj, name)
if hasattr(attr, 'im_self'):
attr = attr.im_self
if hasattr(attr, '__dict__'):
attr = attr.__dict__
name = name + '__roles__'
if name in attr:
roles = attr[name]
break
elif hasattr(obj, '__dict__'):
attr = obj.__dict__
if '__roles__' in attr:
roles = attr['__roles__']
break
if roles:
try:
if 'Shared' not in roles:
return tuple(roles)
roles = list(roles)
roles.remove('Shared')
return roles
except:
return []
if roles is None:
return ['Manager', 'Anonymous']
if roles is _marker:
return ['Manager']
return roles
def setRoles(self, roles):
obj = self.obj
if isinstance(roles, list) and not roles:
if hasattr(obj, self._p):
delattr(obj, self._p)
else:
setattr(obj, self._p, roles)
for name in self.data:
if name=='':
attr = obj
else:
attr = getattr(obj, name)
try:
del attr.__roles__
except:
pass
try:
delattr(obj, name + '__roles__')
except:
pass
def setRole(self, role, present):
roles = self.getRoles()
if role in roles:
if present:
return
if isinstance(roles, list):
roles.remove(role)
else:
roles = list(roles)
roles.remove(role)
roles = tuple(roles)
elif not present:
return
else:
if isinstance(roles, list):
roles.append(role)
else:
roles=roles + (role, )
self.setRoles(roles)
def __len__(self):
return 1
def __str__(self):
return self.name
_registeredPermissions = {}
_ac_permissions = ()
def getPermissions():
return _ac_permissions
def addPermission(perm, default_roles=('Manager', )):
if perm in _registeredPermissions:
return
entry = ((perm, (), default_roles), )
global _ac_permissions
_ac_permissions = _ac_permissions + entry
_registeredPermissions[perm] = 1
mangled = pname(perm) # get mangled permission name
if not hasattr(ApplicationDefaultPermissions, mangled):
setattr(ApplicationDefaultPermissions, mangled, default_roles)
def registerPermissions(permissions, defaultDefault=('Manager', )):
"""Register an __ac_permissions__ sequence.
"""
for setting in permissions:
if setting[0] in _registeredPermissions:
continue
if len(setting)==2:
perm, methods = setting
default = defaultDefault
else:
perm, methods, default = setting
addPermission(perm, default)
class ApplicationDefaultPermissions:
_View_Permission = ('Manager', 'Anonymous')
_Access_contents_information_Permission = ('Manager', 'Anonymous')
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Permission Mapping
Sometimes, we need an object's permissions to be remapped to other permissions
when the object is used in special ways. This is rather hard, since we
need the object's ordinary permissions intact so we can manage it.
"""
from cgi import escape
from Acquisition import ImplicitAcquisitionWrapper
from ExtensionClass import Base
from zope.interface import implements
from AccessControl.class_init import InitializeClass
from AccessControl.interfaces import IPermissionMappingSupport
from AccessControl.owner import UnownableOwner
from AccessControl.Permission import pname
from AccessControl.requestmethod import requestmethod
class RoleManager:
implements(IPermissionMappingSupport)
# XXX: No security declarations?
def manage_getPermissionMapping(self):
"""Return the permission mapping for the object
This is a list of dictionaries with:
permission_name -- The name of the native object permission
class_permission -- The class permission the permission is
mapped to.
"""
wrapper=getattr(self, '_permissionMapper', None)
if wrapper is None: wrapper=PM()
perms={}
for p in self.possible_permissions():
perms[pname(p)]=p
r=[]
a=r.append
for ac_perms in self.ac_inherited_permissions(1):
p=perms.get(getPermissionMapping(ac_perms[0], wrapper), '')
a({'permission_name': ac_perms[0], 'class_permission': p})
return r
@requestmethod('POST')
def manage_setPermissionMapping(self,
permission_names=[],
class_permissions=[], REQUEST=None):
"""Change the permission mapping
"""
wrapper=getattr(self, '_permissionMapper', None)
if wrapper is None: wrapper=PM()
perms=self.possible_permissions()
for i in range(len(permission_names)):
name=permission_names[i]
p=class_permissions[i]
if p and (p not in perms):
__traceback_info__=perms, p, i
raise ValueError, (
"""Attempted to map a permission to a permission, %s,
that is not valid. This should never happen. (Waaa).
""" % escape(p))
setPermissionMapping(name, wrapper, p)
self._permissionMapper=wrapper
if REQUEST is not None:
return self.manage_access(
REQUEST,
manage_tabs_message='The permission mapping has been updated')
def _isBeingUsedAsAMethod(self, REQUEST =None, wannaBe=0):
try:
if hasattr(self, 'aq_self'):
r=self.aq_acquire('_isBeingUsedAsAMethod_')
else:
r=self._isBeingUsedAsAMethod_
except: r=0
if REQUEST is not None:
if not r != (not wannaBe): REQUEST.response.notFoundError()
return r
InitializeClass(RoleManager)
def getPermissionMapping(name, obj, st=type('')):
obj=getattr(obj, 'aq_base', obj)
name=pname(name)
r=getattr(obj, name, '')
if type(r) is not st: r=''
return r
def setPermissionMapping(name, obj, v):
name=pname(name)
if v: setattr(obj, name, pname(v))
elif obj.__dict__.has_key(name): delattr(obj, name)
class PM(Base):
_owner=UnownableOwner
_View_Permission='_View_Permission'
_is_wrapperish = 1
def __getattr__(self, name):
# We want to make sure that any non-explicitly set methods are
# private!
if name.startswith('_') and name.endswith("_Permission"): return ''
raise AttributeError, escape(name)
PermissionMapper=PM
def aqwrap(object, wrapper, parent):
r=Rewrapper()
r._ugh=wrapper, object, parent
return r
class Rewrapper(Base):
def __of__(self, parent):
w, m, p = self._ugh
return m.__of__(
ImplicitAcquisitionWrapper(
w, parent))
def __getattr__(self, name):
w, m, parent = self._ugh
self=m.__of__(
ImplicitAcquisitionWrapper(
w, parent))
return getattr(self, name)
def __call__(self, *args, **kw):
w, m, parent = self._ugh
self=m.__of__(
ImplicitAcquisitionWrapper(
w, parent))
return apply(self, args, kw)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
'''Objects that implement Permission-based roles.
'''
# The following names are inserted by AccessControl.Implementation:
#
# rolesForPermissionOn, PermissionRole, imPermissionRole,
# _what_not_even_god_should_do
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Constant definitions for built-in Zope permissions
"""
access_contents_information='Access contents information'
add_database_methods='Add Database Methods'
add_documents_images_and_files='Add Documents, Images, and Files'
add_external_methods='Add External Methods'
add_folders='Add Folders'
add_mailhost_objects='Add MailHost objects'
add_page_templates='Add Page Templates'
add_python_scripts='Add Python Scripts'
add_user_folders='Add User Folders'
add_vocabularies='Add Vocabularies'
add_z_gadfly_database_connections='Add Z Gadfly Database Connections'
add_zcatalogs='Add ZCatalogs'
add_zope_tutorials='Add Zope Tutorials'
change_database_connections='Change Database Connections'
change_database_methods='Change Database Methods'
change_external_methods='Change External Methods'
change_images_and_files='Change Images and Files'
change_python_scripts='Change Python Scripts'
change_configuration='Change configuration'
change_page_templates='Change Page Templates'
change_permissions='Change permissions'
change_proxy_roles='Change proxy roles'
copy_or_move='Copy or Move'
create_class_instances='Create class instances'
define_permissions='Define permissions'
delete_objects='Delete objects'
edit_factories='Edit Factories'
ftp_access='FTP access'
import_export_objects='Import/Export objects'
manage_vocabulary='Manage Vocabulary'
manage_zcatalog_entries='Manage ZCatalog Entries'
manage_zcatalog_indexes='Manage ZCatalogIndex Entries'
manage_properties='Manage properties'
manage_users='Manage users'
open_close_database_connection='Open/Close Database Connection'
open_close_database_connections='Open/Close Database Connections'
query_vocabulary='Query Vocabulary'
search_zcatalog='Search ZCatalog'
take_ownership='Take ownership'
test_database_connections='Test Database Connections'
undo_changes='Undo changes'
use_database_methods='Use Database Methods'
use_factories='Use Factories'
use_mailhost_services='Use mailhost services'
view='View'
view_history='View History'
view_management_screens='View management screens'
webdav_access='WebDAV access'
webdav_lock_items='WebDAV Lock items'
webdav_unlock_items='WebDAV Unlock items'
from zope.deferredimport import deprecated
new_loc = 'DocumentTemplate.permissions'
deprecated("Please import from %s" % new_loc,
change_dtml_documents = '%s:change_dtml_documents' % new_loc,
change_dtml_methods = '%s:change_dtml_methods' % new_loc,
)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Access control support
"""
# BBB
from .rolemanager import DEFAULTMAXLISTUSERS
from .rolemanager import _isBeingUsedAsAMethod
from .rolemanager import _isNotBeingUsedAsAMethod
from .rolemanager import reqattr
from .rolemanager import classattr
from .rolemanager import instance_dict
from .rolemanager import class_dict
from .rolemanager import instance_attrs
from .rolemanager import class_attrs
from .rolemanager import gather_permissions
from zope.deferredimport import deprecated
deprecated("RoleManager is no longer part of AccessControl, please "
"depend on Zope2 and import from OFS.role or use the new minimal "
"RoleManager class from AccessControl.rolemanager.",
RoleManager = 'OFS.role:RoleManager',
)
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Security management
"""
def getSecurityManager():
"""Get a security manager, for the current thread.
"""
thread_id=get_ident()
manager=_managers.get(thread_id, None)
if manager is None:
nobody = getattr(SpecialUsers, 'nobody', None)
if nobody is None:
# Initialize SpecialUsers by importing User.py.
import User
nobody = SpecialUsers.nobody
manager = SecurityManager(thread_id, SecurityContext(nobody))
_managers[thread_id]=manager
return manager
def setSecurityManager(manager):
"""install *manager* as current security manager for this thread."""
thread_id=get_ident()
_managers[thread_id]=manager
import SpecialUsers
# AccessControl.Implementation inserts SecurityManager.
try:
from thread import get_ident
except ImportError:
def get_ident():
return 0
_managers={}
def newSecurityManager(request, user):
"""Set up a new security context for a request for a user
"""
thread_id=get_ident()
_managers[thread_id]=SecurityManager(
thread_id,
SecurityContext(user),
)
def noSecurityManager():
try: del _managers[get_ident()]
except: pass
def setSecurityPolicy(aSecurityPolicy):
"""Set the system default security policy.
This method should only be caused by system startup code. It should
never, for example, be called during a web request.
"""
SecurityManager.setSecurityPolicy(aSecurityPolicy)
class SecurityContext:
"""The security context is an object used internally to the security
machinery. It captures data about the current security context.
"""
def __init__(self, user):
self.stack=[]
self.user=user
self.objectCache = {}
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
'''API module to set the security policy
'''
from AccessControl import ImplPython as _ImplPython
from AccessControl.SimpleObjectPolicies import _noroles
def setSecurityPolicy(aSecurityPolicy):
"""Set the system default security policy.
This method should only be caused by system startup code. It should
never, for example, be called during a web request.
"""
last = _ImplPython._defaultPolicy
_ImplPython._defaultPolicy = aSecurityPolicy
return last
# AccessControl.Implementation inserts SecurityManager.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
'''Collect rules for access to objects that don\'t have roles.
The rules are expressed as a mapping from type -> assertion
An assertion can be:
- A dict
- A callable
- Something with a truth value
If the assertion is a callable, then it will be called with
a name being accessed and the name used. Its return value is ignored,
but in may veto an access by raising an exception.
If the assertion is a dictionary, then the keys are attribute names.
The values may be callables or objects with boolean values. If a value
is callable, it will be called with the object we are accessing an
attribute of and the attribute name. It should return an attribute
value. Callables are often used to returned guarded versions of
methods. Otherwise, accesses are allowed if values in this dictionary
are true and disallowed if the values are false or if an item for an
attribute name is not present.
If the assertion is not a dict and is not callable, then access to
unprotected attributes is allowed if the assertion is true, and
disallowed otherwise.
XXX This descrition doesn't actually match what's done in ZopeGuards
or in ZopeSecurityPolicy. :(
'''
_noroles = [] # this is imported in various places
import Record
# Allow access to unprotected attributes
Record.Record.__allow_access_to_unprotected_subobjects__=1
# ContainerAssertions are used by cAccessControl to check access to
# attributes of container types, like dict, list, or string.
# ContainerAssertions maps types to a either a dict, a function, or a
# simple boolean value. When guarded_getattr checks the type of its
# first argument against ContainerAssertions, and invokes checking
# logic depending on what value it finds.
# If the value for a type is:
# - a boolean value:
# - the value determines whether access is allowed
# - a function (or callable):
# - The function is called with the name of the attribute and
# the actual attribute value, then the value is returned.
# The function can raise an exception.
# - a dict:
# - The dict maps attribute names to boolean values or functions.
# The boolean values behave as above, but the functions do not.
# The value returned for attribute access is the result of
# calling the function with the object and the attribute name.
ContainerAssertions={
type(()): 1,
type(''): 1,
type(u''): 1,
}
Containers = ContainerAssertions.get
def allow_type(Type, allowed=1):
"""Allow a type and all of its methods and attributes to be used from
restricted code. The argument Type must be a type."""
if type(Type) is not type:
raise ValueError, "%s is not a type" % `Type`
if hasattr(Type, '__roles__'):
raise ValueError, "%s handles its own security" % `Type`
if not (isinstance(allowed, int) or isinstance(allowed, dict)):
raise ValueError, "The 'allowed' argument must be an int or dict."
ContainerAssertions[Type] = allowed
#
# WAAAA!
#
from BTrees.OOBTree import OOBTree, OOBucket, OOSet
from BTrees.OIBTree import OIBTree, OIBucket, OISet
from BTrees.IOBTree import IOBTree, IOBucket, IOSet
from BTrees.IIBTree import IIBTree, IIBucket, IISet
for tree_type, has_values in [(OOBTree, 1),
(OOBucket, 1),
(OOSet, 0),
(OIBTree, 1),
(OIBucket, 1),
(OISet, 0),
(IOBTree, 1),
(IOBucket, 1),
(IOSet, 0),
(IIBTree, 1),
(IIBucket, 1),
(IISet, 0),
]:
tree = tree_type()
key_type = type(tree.keys())
if key_type is not list: # lists have their own declarations
allow_type(key_type)
if has_values:
assert key_type is type(tree.values())
assert key_type is type(tree.items())
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Place to find special users
This is needed to avoid a circular import problem. The 'real' values
are stored here by the AccessControl.User module as part of it's
initialization.
"""
nobody = None
system = None
emergency_user = None
# Note: use of the 'super' name is deprecated.
super = None
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Access control package.
"""
# BBB
from .users import BasicUser
from .users import SimpleUser
from .users import SpecialUser
from .users import User
from .users import UnrestrictedUser
from .users import NullUnrestrictedUser
from .users import readUserAccessFile
from .users import emergency_user
from .users import emergency_user as super
from .users import _remote_user_mode
from .users import nobody
from .users import system
from .users import rolejoin
from .users import addr_match
from .users import host_match
from .users import domainSpecMatch
from .users import absattr
from .users import reqattr
from .users import UnrestrictedUser as Super
from zope.deferredimport import deprecated
deprecated("User folders are no longer part of AccessControl, please depend "
"on Zope2 and import from OFS.userfolder or use the new minimal "
"user folder classes from AccessControl.userfolder.",
BasicUserFolder = 'OFS.userfolder:BasicUserFolder',
manage_addUserFolder = 'OFS.userfolder:manage_addUserFolder',
UserFolder = 'OFS.userfolder:UserFolder',
)
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Define Zope's default security policy
"""
from types import MethodType
# AccessControl.Implementation inserts:
# ZopeSecurityPolicy, getRoles, rolesForPermissionOn
from AccessControl.SimpleObjectPolicies import _noroles
rolesForPermissionOn = None # XXX: avoid import loop
tuple_or_list = tuple, list
def getRoles(container, name, value, default):
global rolesForPermissionOn # XXX: avoid import loop
if rolesForPermissionOn is None:
from PermissionRole import rolesForPermissionOn
roles = getattr(value, '__roles__', _noroles)
if roles is _noroles:
if not name or not isinstance(name, basestring):
return default
if type(value) is MethodType:
container = value.im_self
cls = getattr(container, '__class__', None)
if cls is None:
return default
roles = getattr(cls, name+'__roles__', _noroles)
if roles is _noroles:
return default
value = container
if roles is None or isinstance(roles, tuple_or_list):
return roles
rolesForPermissionOn = getattr(roles, 'rolesForPermissionOn', None)
if rolesForPermissionOn is not None:
roles = rolesForPermissionOn(value)
return roles
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
# This has to happen early so things get initialized properly
from AccessControl.Implementation import setImplementation
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import setSecurityPolicy
from AccessControl.SecurityInfo import ClassSecurityInfo
from AccessControl.SecurityInfo import ModuleSecurityInfo
from AccessControl.SecurityInfo import ACCESS_PRIVATE
from AccessControl.SecurityInfo import ACCESS_PUBLIC
from AccessControl.SecurityInfo import ACCESS_NONE
from AccessControl.SecurityInfo import secureModule
from AccessControl.SecurityInfo import allow_module
from AccessControl.SecurityInfo import allow_class
from AccessControl.SimpleObjectPolicies import allow_type
from AccessControl.unauthorized import Unauthorized # XXX
from AccessControl.ZopeGuards import full_write_guard
from AccessControl.ZopeGuards import safe_builtins
ModuleSecurityInfo('AccessControl').declarePublic('getSecurityManager')
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Class initialization.
"""
import logging
from AccessControl.Permission import ApplicationDefaultPermissions # BBB
def InitializeClass(self):
from AccessControl.Permission import registerPermissions
from AccessControl.PermissionRole import PermissionRole
dict=self.__dict__
have=dict.has_key
ft=type(InitializeClass)
dict_items=dict.items()
for name, v in dict_items:
if getattr(v, '_need__name__', 0):
d = v.__dict__
oldname = d.get('__name__', '')
if d.get('_implicit__name__', 0):
# Already supplied a name.
if name != oldname:
# Tried to implicitly assign a different name!
try: classname = '%s.%s' % (
self.__module__, self.__name__)
except AttributeError: classname = `self`
logging.getLogger("Init").warning(
'Ambiguous name for method of %s: %r != %r',
classname, d['__name__'], name)
else:
# Supply a name implicitly so that the method can
# find the security assertions on its container.
v._implicit__name__ = 1
v.__name__ = name
if name=='manage' or name[:7]=='manage_':
name=name+'__roles__'
if not have(name):
setattr(self, name, ('Manager',))
elif name=='manage' or name[:7]=='manage_' and type(v) is ft:
name=name+'__roles__'
if not have(name):
setattr(self, name, ('Manager',))
# Look for a SecurityInfo object on the class. If found, call its
# apply() method to generate __ac_permissions__ for the class. We
# delete the SecurityInfo from the class dict after it has been
# applied out of paranoia.
for key, value in dict_items:
if hasattr(value, '__security_info__'):
security_info=value
security_info.apply(self)
delattr(self, key)
break
if self.__dict__.has_key('__ac_permissions__'):
registerPermissions(self.__ac_permissions__)
for acp in self.__ac_permissions__:
pname, mnames = acp[:2]
if len(acp) > 2:
roles = acp[2]
pr = PermissionRole(pname, roles)
else:
pr = PermissionRole(pname)
for mname in mnames:
setattr(self, mname+'__roles__', pr)
if (mname and mname not in ('context', 'request') and
not hasattr(self, mname)):
# don't complain about context or request, as they are
# frequently not available as class attributes
logging.getLogger("Init").warning(
"Class %s.%s has a security declaration for "
"nonexistent method %r", self.__module__,
self.__name__, mname)
default__class_init__ = InitializeClass # BBB: old name
<configure xmlns="http://namespaces.zope.org/zope">
<include file="permissions.zcml"/>
</configure>
This diff is collapsed.
# A wrapper to replace the usage of the zLOG module in cAccessControl without
# having the need to change the C code significantly.
from logging import getLogger
LOG = getLogger('AccessControl')
warn = LOG.warn
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<include package="zope.component" file="meta.zcml" />
<include package="zope.security" file="meta.zcml" />
<meta:directives namespace="http://namespaces.zope.org/zope">
<meta:complexDirective
name="class"
schema="zope.security.metadirectives.IClassDirective"
handler=".metaconfigure.ClassDirective"
>
<meta:subdirective
name="implements"
schema="zope.security.metadirectives.IImplementsSubdirective"
/>
<meta:subdirective
name="require"
schema="zope.security.metadirectives.IRequireSubdirective"
/>
<meta:subdirective
name="allow"
schema="zope.security.metadirectives.IAllowSubdirective"
/>
</meta:complexDirective>
<meta:directive
name="securityPolicy"
schema="zope.security.zcml.ISecurityPolicyDirective"
handler="zope.security.zcml.securityPolicy"
/>
</meta:directives>
</configure>
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
import warnings
from zope.security import metaconfigure
from AccessControl.class_init import InitializeClass
from AccessControl.security import protectName
class ClassDirective(metaconfigure.ClassDirective):
def __protectName(self, name, permission_id):
self.__context.action(
discriminator = ('five:protectName', self.__class, name),
callable = protectName,
args = (self.__class, name, permission_id)
)
def __protectSetAttributes(self, names, permission_id):
warnings.warn("The set_attribute option of the <require /> directive "
"is not supported in Zope 2. "
"Ignored for %s" % str(self.__class), stacklevel=3)
def __protectSetSchema(self, schema, permission):
warnings.warn("The set_schema option of the <require /> directive "
"is not supported in Zope 2. "
"Ignored for %s" % str(self.__class), stacklevel=3)
def __mimic(self, _context, class_):
warnings.warn("The like_class option of the <require /> directive "
"is not supported in Zope 2. "
"Ignored for %s" % str(self.__class), stacklevel=3)
def __call__(self):
return self.__context.action(
discriminator = None,
callable = InitializeClass,
args = (self.__class,)
)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
"""Support for owned objects
"""
from Acquisition import aq_base
from Acquisition import aq_get
from Acquisition import aq_inner
from Acquisition import aq_parent
from ExtensionClass import Base
from zope.interface import implements
from AccessControl.class_init import InitializeClass
from AccessControl.interfaces import IOwned
from AccessControl.Permissions import view_management_screens
from AccessControl.Permissions import take_ownership
from AccessControl.SecurityInfo import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager
# avoid importing 'emergency_user' / 'nobody' before set
from AccessControl import SpecialUsers as SU
UnownableOwner=[]
def ownableFilter(self):
_owner = aq_get(self, '_owner', None, 1)
return _owner is not UnownableOwner
# Marker to use as a getattr default.
_mark=ownableFilter
class Owned(Base):
implements(IOwned)
security = ClassSecurityInfo()
security.setPermissionDefault(take_ownership, ('Owner', ))
security.declareProtected(view_management_screens, 'owner_info')
def owner_info(self):
"""Get ownership info for display
"""
owner=self.getOwnerTuple()
if owner is None or owner is UnownableOwner:
return owner
d={'path': '/'.join(owner[0]), 'id': owner[1],
'explicit': hasattr(self, '_owner'),
'userCanChangeOwnershipType':
getSecurityManager().checkPermission('Take ownership', self)
}
return d
security.declarePrivate('getOwner')
def getOwner(self, info=0,
aq_get=aq_get,
UnownableOwner=UnownableOwner,
getSecurityManager=getSecurityManager,
):
"""Get the owner
If a true argument is provided, then only the owner path and id are
returned. Otherwise, the owner object is returned.
"""
if info:
import warnings
warnings.warn('Owned.getOwner(1) is deprecated; '
'please use getOwnerTuple() instead.',
DeprecationWarning, stacklevel=2)
owner=aq_get(self, '_owner', None, 1)
if info or (owner is None): return owner
if owner is UnownableOwner: return None
udb, oid = owner
root=self.getPhysicalRoot()
udb=root.unrestrictedTraverse(udb, None)
if udb is None:
user = SU.nobody
else:
user = udb.getUserById(oid, None)
if user is None: user = SU.nobody
return user
security.declarePrivate('getOwnerTuple')
def getOwnerTuple(self):
"""Return a tuple, (userdb_path, user_id) for the owner.
o Ownership can be acquired, but only from the containment path.
o If unowned, return None.
"""
return aq_get(self, '_owner', None, 1)
security.declarePrivate('getWrappedOwner')
def getWrappedOwner(self):
"""Get the owner, modestly wrapped in the user folder.
o If the object is not owned, return None.
o If the owner's user database doesn't exist, return Nobody.
o If the owner ID does not exist in the user database, return Nobody.
"""
owner = self.getOwnerTuple()
if owner is None or owner is UnownableOwner:
return None
udb_path, oid = owner
root = self.getPhysicalRoot()
udb = root.unrestrictedTraverse(udb_path, None)
if udb is None:
return SU.nobody
user = udb.getUserById(oid, None)
if user is None:
return SU.nobody
return user.__of__(udb)
security.declarePrivate('changeOwnership')
def changeOwnership(self, user, recursive=0):
"""Change the ownership to the given user.
If 'recursive' is true then also take ownership of all sub-objects,
otherwise sub-objects retain their ownership information.
"""
new = ownerInfo(user)
if new is None:
return # Special user!
old = self.getOwnerTuple()
if not recursive:
if old == new or old is UnownableOwner:
return
if recursive:
children = getattr( aq_base(self), 'objectValues', lambda :() )()
for child in children:
child.changeOwnership(user, 1)
if old is not UnownableOwner:
self._owner = new
def userCanTakeOwnership(self):
security=getSecurityManager()
user=security.getUser()
info=ownerInfo(user)
if info is None: return 0
owner=self.getOwnerTuple()
if owner == info: return 0
return security.checkPermission('Take ownership', self)
def _deleteOwnershipAfterAdd(self):
# Only delete _owner if it is an instance attribute.
if self.__dict__.get('_owner', _mark) is not _mark:
del self._owner
for object in self.objectValues():
try: s=object._p_changed
except: s=0
try: object._deleteOwnershipAfterAdd()
except: pass
if s is None: object._p_deactivate()
def manage_fixupOwnershipAfterAdd(self):
# Sigh, get the parent's _owner
parent=getattr(self, '__parent__', None)
if parent is not None: _owner=aq_get(parent, '_owner', None, 1)
else: _owner=None
if (_owner is None and
((getattr(self, '__parent__', None) is None) or
(not hasattr(self, 'getPhysicalRoot'))
)
):
# This is a special case. An object is
# being added to an object that hasn't
# been added to the object hierarchy yet.
# We can delay fixing up the ownership until the
# object is actually added.
return None
if _owner is UnownableOwner:
# We want to acquire Unownable ownership!
return self._deleteOwnershipAfterAdd()
else:
# Otherwise change the ownership
user=getSecurityManager().getUser()
if (SU.emergency_user and aq_base(user) is SU.emergency_user):
__creatable_by_emergency_user__=getattr(
self,'__creatable_by_emergency_user__', None)
if (__creatable_by_emergency_user__ is None or
(not __creatable_by_emergency_user__())):
raise EmergencyUserCannotOwn(
"Objects cannot be owned by the emergency user")
self.changeOwnership(user)
# Force all subs to acquire ownership!
for object in self.objectValues():
try: s=object._p_changed
except: s=0
try: object._deleteOwnershipAfterAdd()
except: pass
if s is None: object._p_deactivate()
InitializeClass(Owned)
class EmergencyUserCannotOwn(Exception):
"The emergency user cannot own anything"
class EditUnowned(Exception):
"Can't edit unowned executables"
def absattr(attr):
if callable(attr): return attr()
return attr
def ownerInfo(user, getattr=getattr):
if user is None:
return None
uid=user.getId()
if uid is None: return uid
db=aq_parent(aq_inner(user))
path=[absattr(db.id)]
root=db.getPhysicalRoot()
while 1:
db=getattr(db,'aq_inner', None)
if db is None: break
db=aq_parent(db)
if db is root: break
id=db.id
if not isinstance(id, str):
try: id=id()
except: id=str(id)
path.append(id)
path.reverse()
return path, uid
<configure xmlns="http://namespaces.zope.org/zope"
i18n_domain="Zope2">
<!-- Create permissions declared in ZCML if they don't exist already -->
<subscriber
for="zope.security.interfaces.IPermission
zope.component.interfaces.IRegistered"
handler=".security.create_permission_from_permission_directive"
/>
<permission
id="zope2.Public"
title="Public, everyone can access"
/>
<permission
id="zope2.Private"
title="Private, only accessible from trusted code"
/>
<permission
id="zope2.AccessContentsInformation"
title="Access contents information"
/>
<permission
id="zope2.ChangeImagesFiles"
title="Change Images and Files"
/>
<permission
id="zope2.ChangeConfig"
title="Change configuration"
/>
<permission
id="zope2.ChangePermissions"
title="Change permissions"
/>
<permission
id="zope2.CopyOrMove"
title="Copy or Move"
/>
<permission
id="zope2.DefinePermissions"
title="Define permissions"
/>
<permission
id="zope2.DeleteObjects"
title="Delete objects"
/>
<permission
id="zope2.FTPAccess"
title="FTP access"
/>
<permission
id="zope2.ImportExport"
title="Import/Export objects"
/>
<permission
id="zope2.ManageProperties"
title="Manage properties"
/>
<permission
id="zope2.ManageUsers"
title="Manage users"
/>
<permission
id="zope2.Undo"
title="Undo changes"
/>
<permission
id="zope2.View"
title="View"
/>
<permission
id="zope2.ViewHistory"
title="View History"
/>
<permission
id="zope2.ViewManagementScreens"
title="View management screens"
/>
<permission
id="zope2.WebDAVLock"
title="WebDAV Lock items"
/>
<permission
id="zope2.WebDAVUnlock"
title="WebDAV Unlock items"
/>
<permission
id="zope2.WebDAVAccess"
title="WebDAV access"
/>
</configure>
#############################################################################
#
# Copyright (c) 2007 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################
import inspect
from zExceptions import Forbidden
from zope.publisher.interfaces.browser import IBrowserRequest
_default = []
def _buildFacade(name, spec, docstring):
"""Build a facade function, matching the decorated method in signature.
Note that defaults are replaced by _default, and _curried will reconstruct
these to preserve mutable defaults.
"""
args = inspect.formatargspec(formatvalue=lambda v: '=_default', *spec)
callargs = inspect.formatargspec(formatvalue=lambda v: '', *spec)
return 'def %s%s:\n """%s"""\n return _curried%s' % (
name, args, docstring, callargs)
def requestmethod(*methods):
"""Create a request method specific decorator"""
methods = map(lambda m: m.upper(), methods)
if len(methods) > 1:
methodsstr = ', '.join(methods[:-1])
methodsstr += ' or ' + methods[-1]
else:
methodsstr = methods[0]
def _methodtest(callable):
"""Only allow callable when request method is %s.""" % methodsstr
spec = inspect.getargspec(callable)
args, defaults = spec[0], spec[3]
try:
r_index = args.index('REQUEST')
except ValueError:
raise ValueError('No REQUEST parameter in callable signature')
arglen = len(args)
if defaults is not None:
defaults = zip(args[arglen - len(defaults):], defaults)
arglen -= len(defaults)
def _curried(*args, **kw):
request = args[r_index]
if IBrowserRequest.providedBy(request):
if request.method not in methods:
raise Forbidden('Request must be %s' % methodsstr)
# Reconstruct keyword arguments
if defaults is not None:
args, kwparams = args[:arglen], args[arglen:]
for positional, (key, default) in zip(kwparams, defaults):
if positional is _default:
kw[key] = default
else:
kw[key] = positional
return callable(*args, **kw)
# Build a facade, with a reference to our locally-scoped _curried
name = callable.__name__
facade_globs = dict(_curried=_curried, _default=_default)
exec _buildFacade(name, spec, callable.__doc__) in facade_globs
return facade_globs[name]
return _methodtest
# For Zope versions 2.8 - 2.10
postonly = requestmethod('POST')
__all__ = ('requestmethod', 'postonly')
Request method decorators
=========================
Using request method decorators, you can limit functions or methods to only
be callable when the HTTP request was made using a particular method.
To limit access to a function or method to POST requests, use the requestmethod
decorator factory::
>>> from AccessControl.requestmethod import requestmethod
>>> @requestmethod('POST')
... def foo(bar, REQUEST):
... return bar
When this method is accessed through a request that does not use POST, the
Forbidden exception will be raised::
>>> foo('spam', GET)
Traceback (most recent call last):
...
Forbidden: Request must be POST
Only when the request was made using POST, will the call succeed::
>>> foo('spam', POST)
'spam'
It doesn't matter if REQUEST is a positional or a keyword parameter::
>>> @requestmethod('POST')
... def foo(bar, REQUEST=None):
... return bar
>>> foo('spam', REQUEST=GET)
Traceback (most recent call last):
...
Forbidden: Request must be POST
*Not* passing an optional REQUEST always succeeds::
>>> foo('spam')
'spam'
Note that the REQUEST parameter is a requirement for the decorator to operate,
not including it in the callable signature results in an error::
>>> @requestmethod('POST')
... def foo(bar):
... return bar
Traceback (most recent call last):
...
ValueError: No REQUEST parameter in callable signature
Because the Zope Publisher uses introspection to match REQUEST variables
against callable signatures, the result of the decorator must match the
original closely, and keyword parameter defaults must be preserved::
>>> import inspect
>>> mutabledefault = dict()
>>> @requestmethod('POST')
... def foo(bar, baz=mutabledefault, egg=mutabledefault, REQUEST=None, *args):
... return bar, baz is mutabledefault, egg is None, REQUEST
>>> inspect.getargspec(foo)[:3]
(['bar', 'baz', 'egg', 'REQUEST'], 'args', None)
>>> foo('spam', egg=None)
('spam', True, True, None)
>>> foo(monty='python')
Traceback (most recent call last):
...
TypeError: foo() got an unexpected keyword argument 'monty'
The requestmethod decorator factory can be used for any request method, simply
pass in the desired request method::
>>> @requestmethod('PUT')
... def foo(bar, REQUEST=None):
... return bar
>>> foo('spam', GET)
Traceback (most recent call last):
...
Forbidden: Request must be PUT
You can pass in multiple request methods allow access by any of them::
>>> @requestmethod('GET', 'HEAD', 'PROPFIND')
... def foo(bar, REQUEST=None):
... return bar
>>> foo('spam', GET)
'spam'
>>> foo('spam', POST)
Traceback (most recent call last):
...
Forbidden: Request must be GET, HEAD or PROPFIND
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2004-2009 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Security handling
"""
from zope.component import getUtility
from zope.component import queryUtility
from zope.interface import classProvides
from zope.interface import implements
from zope.security.checker import CheckerPublic
from zope.security.interfaces import IInteraction
from zope.security.interfaces import ISecurityPolicy
from zope.security.interfaces import IPermission
from zope.security.management import thread_local
from zope.security.simplepolicies import ParanoidSecurityPolicy
from AccessControl.SecurityInfo import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.Permission import addPermission
CheckerPublicId = 'zope.Public'
CheckerPrivateId = 'zope2.Private'
def getSecurityInfo(klass):
sec = {}
info = vars(klass)
if info.has_key('__ac_permissions__'):
sec['__ac_permissions__'] = info['__ac_permissions__']
for k, v in info.items():
if k.endswith('__roles__'):
sec[k] = v
return sec
def clearSecurityInfo(klass):
info = vars(klass)
if info.has_key('__ac_permissions__'):
delattr(klass, '__ac_permissions__')
for k, v in info.items():
if k.endswith('__roles__'):
delattr(klass, k)
def checkPermission(permission, object, interaction=None):
"""Return whether security policy allows permission on object.
Arguments:
permission -- A permission name
object -- The object being accessed according to the permission
interaction -- This zope.security concept has no equivalent in Zope 2,
and is ignored.
checkPermission is guaranteed to return True if permission is
CheckerPublic or None.
"""
if (permission in ('zope.Public', 'zope2.Public') or
permission is None or permission is CheckerPublic):
return True
if isinstance(permission, basestring):
permission = queryUtility(IPermission, unicode(permission))
if permission is None:
return False
if getSecurityManager().checkPermission(permission.title, object):
return True
return False
class SecurityPolicy(ParanoidSecurityPolicy):
"""Security policy that bridges between zope.security security mechanisms
and Zope 2's security policy.
Don't let the name of the base class fool you... This really just
delegates to Zope 2's security manager."""
classProvides(ISecurityPolicy)
implements(IInteraction)
def checkPermission(self, permission, object):
return checkPermission(permission, object)
def newInteraction():
"""Con zope.security to use Zope 2's checkPermission.
zope.security when it does a checkPermission will turn around and
ask the thread local interaction for the checkPermission method.
By making the interaction *be* Zope 2's security manager, we can
con zope.security into using Zope 2's checker...
"""
if getattr(thread_local, 'interaction', None) is None:
thread_local.interaction = SecurityPolicy()
def _getSecurity(klass):
# a Zope 2 class can contain some attribute that is an instance
# of ClassSecurityInfo. Zope 2 scans through things looking for
# an attribute that has the name __security_info__ first
info = vars(klass)
for k, v in info.items():
if hasattr(v, '__security_info__'):
return v
# we stuff the name ourselves as __security__, not security, as this
# could theoretically lead to name clashes, and doesn't matter for
# zope 2 anyway.
security = ClassSecurityInfo()
setattr(klass, '__security__', security)
return security
def protectName(klass, name, permission_id):
"""Protect the attribute 'name' on 'klass' using the given
permission"""
security = _getSecurity(klass)
# Zope 2 uses string, not unicode yet
name = str(name)
if permission_id == CheckerPublicId or permission_id is CheckerPublic:
# Sometimes, we already get a processed permission id, which
# can mean that 'zope.Public' has been interchanged for the
# CheckerPublic object
security.declarePublic(name)
elif permission_id == CheckerPrivateId:
security.declarePrivate(name)
else:
permission = getUtility(IPermission, name=permission_id)
# Zope 2 uses string, not unicode yet
perm = str(permission.title)
security.declareProtected(perm, name)
def protectClass(klass, permission_id):
"""Protect the whole class with the given permission"""
security = _getSecurity(klass)
if permission_id == CheckerPublicId or permission_id is CheckerPublic:
# Sometimes, we already get a processed permission id, which
# can mean that 'zope.Public' has been interchanged for the
# CheckerPublic object
security.declareObjectPublic()
elif permission_id == CheckerPrivateId:
security.declareObjectPrivate()
else:
permission = getUtility(IPermission, name=permission_id)
# Zope 2 uses string, not unicode yet
perm = str(permission.title)
security.declareObjectProtected(perm)
def create_permission_from_permission_directive(permission, event):
"""When a new IPermission utility is registered (via the <permission />
directive), create the equivalent Zope2 style permission.
"""
# Zope 2 uses string, not unicode yet
zope2_permission = str(permission.title)
addPermission(zope2_permission)
This diff is collapsed.
This diff is collapsed.
# test module, partially private
def priv():
pass
def pub():
pass
# test module, all private
def priv():
pass
# test module, public
from Acquisition import aq_base # Foil the old ZopeSecurityPolicy
def pub():
pass
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import unittest
class TestRoleManager(unittest.TestCase):
def test_interfaces(self):
from AccessControl.interfaces import IPermissionMappingSupport
from AccessControl.PermissionMapping import RoleManager
from zope.interface.verify import verifyClass
verifyClass(IPermissionMappingSupport, RoleManager)
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestRoleManager),
))
This diff is collapsed.
import unittest
class TestRoleManager(unittest.TestCase):
def test_interfaces(self):
from AccessControl.interfaces import IRoleManager
from AccessControl.rolemanager import RoleManager
from zope.interface.verify import verifyClass
verifyClass(IRoleManager, RoleManager)
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestRoleManager),
))
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -4,6 +4,7 @@ versions = versions
[versions]
# Zope2-specific
Zope2 =
AccessControl = 2.13.0
Acquisition = 2.13.3
DateTime = 2.12.2
ExtensionClass = 2.13.2
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment