[Zope3-dev] Symbolic Constants for security settings
Steve Alexander
steve@cat-box.net
Wed, 20 Mar 2002 17:41:40 +0000
Hi folks,
You may recall that a few weeks ago, I found a bug where strings were
being used as symbolic constants in App/Security/Settings.py, and these
lost identity when pickled and unpickled.
This was a problem because the security checking system, and the
security UI, is written assuming that you can compare these symbolic
constants by identity.
I fixed the problem by having each symbolic constant as its own class
(that can't be instantiated), and implementing useful __str__, etc
methods in a meta-class.
Well, I've revisited this code, and I'm thinking of checking in the
following to replace it:
----
# Settings.py
#
# Copyright (c) 2001 Zope Coporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 1.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 setting constants """
class PermissionSetting(object):
"""PermissionSettings should be considered as immutable.
They can be compared by identity. They are identified by
their name.
"""
def __new__(cls, name, description=None):
"""Keep a dict of PermissionSetting instances, indexed by
name. If the name already exists in the dict, return that
instance rather than creating a new one.
"""
instances = cls.__dict__.get('__instances__')
if instances is None:
cls.__instances__ = instances = {}
it = instances.get(name)
if it is None:
instances[name] = it = object.__new__(cls)
it._init(name, description)
return it
def _init(self, name, description):
self.__name = name
self.__description = description
def getDescription(self):
return self.__description
def getName(self):
return self.__name
def __str__(self):
return "PermissionSetting: %s" % self.__name
# register PermissionSettings to be symbolic constants by identity,
# even when pickled and unpickled.
import copy_reg
copy_reg.constructor(PermissionSetting)
copy_reg.pickle(PermissionSetting, lambda ps: ps.getName(),
PermissionSetting)
Allow = PermissionSetting('Allow',
'Explicit allow setting for permissions')
Deny = PermissionSetting('Deny',
'Explicit deny setting for permissions')
Unset = PermissionSetting('Unset',
'Unset constant that denotes no setting for permission and role')
Assign = PermissionSetting('Assign',
'Explicit assign setting for roles')
Remove = PermissionSetting('Remove',
'Explicit remove setting for roles')
----
I've exchanged a fancy meta-class + class arrangement for a fancy
__new__ + copy_reg arrangement.
I think this version is clearer, and it has fewer lines of code.
However, it is marginally less efficient when pickling and unpickling;
although I think the difference really is marginal.
It is no different in terms of how fast these constants can be processed
by the security machinery.
I've also noticed that there are no unit test checking that these
symbolic constants retain identity when pickled and unpickled. I'll add
a test for that in any case.
This change will cause any existing Zope 3 TTW security mappings to be
ignored, as if they are not set.
Any comments?
If no-one screams, I'll check this in in a few hours.
--
Steve Alexander