def test_check_setattr(self): oldinst = OldInst() oldinst.d = OldInst() newinst = NewInst() newinst.d = NewInst() for inst in oldinst, newinst: checker = Checker({}, {'a': 'perm', 'z': 'perm'}) self.assertRaises(Unauthorized, checker.check_setattr, inst, 'a') self.assertRaises(Unauthorized, checker.check_setattr, inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'c') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': 'test_allowed', 'z': 'test_allowed'}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': CheckerPublic, 'z': CheckerPublic}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f')
def setUp(test): zope.component.testing.setUp(test) zope.component.provideAdapter(xmlrpc.ListPreMarshaller) zope.component.provideAdapter(xmlrpc.TuplePreMarshaller) zope.component.provideAdapter(xmlrpc.BinaryPreMarshaller) zope.component.provideAdapter(xmlrpc.FaultPreMarshaller) zope.component.provideAdapter(xmlrpc.DateTimePreMarshaller) zope.component.provideAdapter(xmlrpc.PythonDateTimePreMarshaller) zope.component.provideAdapter(xmlrpc.DictPreMarshaller) defineChecker( xmlrpclib.Binary, Checker( { 'data': CheckerPublic, 'decode': CheckerPublic, 'encode': CheckerPublic }, {})) defineChecker( xmlrpclib.Fault, Checker({ 'faultCode': CheckerPublic, 'faultString': CheckerPublic }, {})) defineChecker(xmlrpclib.DateTime, Checker({'value': CheckerPublic}, {}))
def testLayeredProxies(self): """Tests that a Proxy will not be re-proxied.""" class Base: __Security_checker__ = NamesChecker(['__Security_checker__']) base = Base() checker = Checker({}) # base is not proxied, so we expect a proxy proxy1 = checker.proxy(base) self.assert_(type(proxy1) is Proxy) self.assert_(getProxiedObject(proxy1) is base) # proxy is a proxy, so we don't expect to get another proxy2 = checker.proxy(proxy1) self.assert_(proxy2 is proxy1) self.assert_(getProxiedObject(proxy2) is base)
def protectLikeUnto(class_, like_unto): """Use the protections from like_unto for class_""" unto_checker = getCheckerForInstancesOf(like_unto) if unto_checker is None: return # We know a dictionary was used because we set it # Note however, that if a checker was created manually # and the caller used say NamesChecker or MultiChecker, # then set_permissions may be None here as Checker # defaults a missing set_permissions parameter to None. # Jim says this doensn't happens with the C version of the # checkers because they use a 'shared dummy dict'. unto_get_protections = unto_checker.get_permissions unto_set_protections = unto_checker.set_permissions checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) get_protections = checker.get_permissions for name in unto_get_protections: get_protections[name] = unto_get_protections[name] set_protections = checker.set_permissions for name in unto_set_protections: set_protections[name] = unto_set_protections[name]
def __call__(self, require=None): if self.name is None: return if self.defaultLanguage not in self.__data: raise ConfigurationError( "A translation for the default language (%s) " "must be specified" % self.defaultLanguage) permission = self.permission factory = I18nFileResourceFactory(self.__data, self.defaultLanguage) if permission: if require is None: require = {} if permission == 'zope.Public': permission = CheckerPublic if require: checker = Checker(require) factory = self._proxyFactory(factory, checker) self._context.action(discriminator=('i18n-resource', self.name, self.type, self.layer), callable=handler, args=('registerAdapter', factory, (self.layer, ), Interface, self.name, self._context.info))
def PreferenceGroupChecker(instance): """A function that generates a custom security checker. The attributes available in a preference group are dynamically generated based on the group schema and the available sub-groups. Thus, the permission dictionaries have to be generated at runtime and are unique for each preference group instance. """ read_perm_dict = {} write_perm_dict = {} # Make sure that the attributes from IPreferenceGroup and IReadContainer # are public. for attrName in ('__id__', '__schema__', '__title__', '__description__', 'get', 'items', 'keys', 'values', '__getitem__', '__contains__', '__iter__', '__len__'): read_perm_dict[attrName] = CheckerPublic # Make the attributes generated from the schema available as well. if instance.__schema__ is not None: for name in getFields(instance.__schema__): read_perm_dict[name] = CheckerPublic write_perm_dict[name] = CheckerPublic # Make all sub-groups available as well. for name in instance.keys(): read_perm_dict[name] = CheckerPublic write_perm_dict[name] = CheckerPublic return Checker(read_perm_dict, write_perm_dict)
def test_w_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import _checkers checker = _checkers[Foo] = Checker({}) permission = object() self._callFUT(Foo, 'bar', permission) self.assertTrue(_checkers[Foo] is checker) self.assertTrue(checker.set_permissions['bar'] is permission)
def test_w_existing_like_unto_checker_w_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import defineChecker from zope.security.checker import _checkers permission1, permission2 = object(), object() foo_checker = Checker({'bar': CheckerPublic}, {'bar': permission2}) defineChecker(Foo, foo_checker) bar_checker = Checker({'bar': permission1, 'baz': CheckerPublic}, {}) defineChecker(Bar, bar_checker) self._callFUT(Bar, Foo) bar_checker = _checkers[Bar] self.assertEqual(bar_checker.get_permissions, { 'bar': CheckerPublic, 'baz': CheckerPublic }) self.assertEqual(bar_checker.set_permissions, foo_checker.set_permissions)
def handle_security(class_, permission, interfaces, attributes): required = set(attributes) for ifc in interfaces: required.update(set(ifc)) if permission == 'zope.Public': permission = CheckerPublic defineChecker(class_, Checker(dict.fromkeys(required, permission)))
def test_check_w_existing_module_checker_zope_Public(self): from zope.security import tests as module from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import _checkers before = _checkers[module] = Checker({'other': CheckerPublic}) self._callFUT(module, 'name', zope_Public) checker = _checkers[module] self.assertTrue(checker is before) self.assertTrue(checker.get_permissions['name'] is CheckerPublic)
def testAllAllowed(self): defineChecker( C, Checker({ 'folder': CheckerPublic, 'item': CheckerPublic, })) tr = Traverser(ProxyFactory(self.root)) item = self.item self.assertEqual(tr.traverse(('', 'folder', 'item')), item) self.assertEqual(tr.traverse(('folder', 'item')), item)
def testItemDenied(self): endInteraction() newInteraction(ParticipationStub('no one')) defineChecker(C, Checker({'item': 'Waaaa', 'folder': CheckerPublic})) tr = Traverser(ProxyFactory(self.root)) folder = self.folder self.assertRaises(Unauthorized, tr.traverse, ('', 'folder', 'item')) self.assertRaises(Unauthorized, tr.traverse, ('folder', 'item')) self.assertEqual(tr.traverse(('', 'folder')), folder) self.assertEqual(tr.traverse(('folder', '..', 'folder')), folder) self.assertEqual(tr.traverse(('folder', )), folder)
def __call__(self): # Set up the utility with an appropriate proxy. # Note that this does not take into account other security # directives on this content made later on during the execution # of the zcml. checker = Checker(self.permission_collector.get_permissions, self.permission_collector.set_permissions) component = ProxyFactory(self.component, checker=checker) utility(self._context, self.provides, component=component, name=self.name) return ()
def protectName(class_, name, permission): """Set a permission on a particular name.""" checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) if permission == 'zope.Public': # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary was used because we set it protections = checker.get_permissions protections[name] = permission
class TestMixinDecoratedChecker(TestCase): def decoratedSetUp(self): self.policy = RecordedSecurityPolicy self._oldpolicy = setSecurityPolicy(self.policy) newInteraction() self.interaction = getInteraction() self.obj = object() def decoratedTearDown(self): endInteraction() setSecurityPolicy(self._oldpolicy) def check_checking_impl(self, checker): o = self.obj checker.check_getattr(o, 'both_get_set') self.assert_(self.interaction.checkChecked(['dc_get_permission'])) checker.check_getattr(o, 'c_only') self.assert_(self.interaction.checkChecked(['get_permission'])) checker.check_getattr(o, 'd_only') self.assert_(self.interaction.checkChecked(['dc_get_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_getattr, o, 'completely_different_attr') self.assert_(self.interaction.checkChecked([])) checker.check(o, '__str__') self.assert_(self.interaction.checkChecked(['get_permission'])) checker.check_setattr(o, 'both_get_set') self.assert_(self.interaction.checkChecked(['dc_set_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'c_only') self.assert_(self.interaction.checkChecked([])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'd_only') self.assert_(self.interaction.checkChecked([])) originalChecker = NamesChecker(['both_get_set', 'c_only', '__str__'], 'get_permission') decorationSetMap = {'both_get_set': 'dc_set_permission'} decorationGetMap = { 'both_get_set': 'dc_get_permission', 'd_only': 'dc_get_permission' } overridingChecker = Checker(decorationGetMap, decorationSetMap)
def protectModule(module, name, permission): """Set up a module checker to require a permission to access a name If there isn't a checker for the module, create one. """ checker = moduleChecker(module) if checker is None: checker = Checker({}, {}) defineChecker(module, checker) if permission == 'zope.Public': # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary get method was used because we set it protections = checker.get_permissions protections[name] = permission
def _checker(_context, permission, allowed_interface, allowed_attributes): if (not allowed_attributes) and (not allowed_interface): allowed_attributes = ["__call__"] if permission == PublicPermission: permission = CheckerPublic require={} if allowed_attributes: for name in allowed_attributes: require[name] = permission if allowed_interface: for i in allowed_interface: for name in i.names(all=True): require[name] = permission checker = Checker(require) return checker
def protectSetAttribute(class_, name, permission): """Set a permission on a particular name.""" checker = getCheckerForInstancesOf(class_) if checker is None: checker = Checker({}, {}) defineChecker(class_, checker) if permission == zope_Public: # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary was used because we set it # Note however, that if a checker was created manually # and the caller used say NamesChecker or MultiChecker, # then set_permissions may be None here as Checker # defaults a missing set_permissions parameter to None. # Jim says this doensn't happens with the C version of the # checkers because they use a 'shared dummy dict'. protections = checker.set_permissions protections[name] = permission
def registerViewCallable(self, view_callable): """Return a URL traversing to which will call `view_callable`. :param view_callable: Will be called with no arguments during traversal. """ # This method is completely out of control. Thanks, Zope. name = '+' + self.factory.getUniqueString() class new_class(simple): def __init__(self, context, request): self.context = context view_callable() required = {} for n in ('browserDefault', '__call__', 'publishTraverse'): required[n] = CheckerPublic defineChecker(new_class, Checker(required)) getSiteManager().registerAdapter( new_class, (ILaunchpadRoot, IDefaultBrowserLayer), Interface, name) self.addCleanup( getSiteManager().unregisterAdapter, new_class, (ILaunchpadRoot, IDefaultBrowserLayer), Interface, name) return 'https://launchpad.dev/' + name
def __call__(self): (_context, name, for_, permission, layer, class_, allowed_interface, allowed_attributes) = self.args required = {} cdict = {} pages = {} for pname, attribute, template in self.pages: if template: cdict[pname] = ViewPageTemplateFile(template) if attribute and attribute != name: cdict[attribute] = cdict[pname] else: if not hasattr(class_, attribute): raise ConfigurationError("Undefined attribute", attribute) attribute = attribute or pname required[pname] = permission pages[pname] = attribute # This should go away, but noone seems to remember what to do. :-( if hasattr(class_, 'publishTraverse'): def publishTraverse(self, request, name, pages=pages, getattr=getattr): if name in pages: return getattr(self, pages[name]) view = queryMultiAdapter((self, request), name=name) if view is not None: return view m = class_.publishTraverse.__get__(self) return m(request, name) else: def publishTraverse(self, request, name, pages=pages, getattr=getattr): if name in pages: return getattr(self, pages[name]) view = queryMultiAdapter((self, request), name=name) if view is not None: return view raise NotFound(self, name, request) cdict['publishTraverse'] = publishTraverse if not hasattr(class_, 'browserDefault'): if self.default or self.pages: default = self.default or self.pages[0][0] cdict['browserDefault'] = ( lambda self, request, default=default: (self, (default, ))) elif providesCallable(class_): cdict['browserDefault'] = (lambda self, request: (self, ())) if class_ is not None: bases = (class_, simple) else: bases = (simple, ) try: cname = str(name) except: cname = "GeneratedClass" cdict['__name__'] = name newclass = type(cname, bases, cdict) for n in ('publishTraverse', 'browserDefault', '__call__'): required[n] = permission _handle_allowed_interface(_context, allowed_interface, permission, required) _handle_allowed_attributes(_context, allowed_attributes, permission, required) _handle_for(_context, for_) defineChecker(newclass, Checker(required)) if self.provides is not None: _context.action(discriminator=None, callable=provideInterface, args=('', self.provides)) _context.action( discriminator=('view', (for_, layer), name, self.provides), callable=handler, args=('registerAdapter', newclass, (for_, layer), self.provides, name, _context.info), )
def jsonrpc(_context, for_=None, interface=None, methods=None, class_=None, permission=None, name=None, layer=None): interface = interface or [] methods = methods or [] if layer is not None: if not layer.extends(interfaces.IJSONRPCRequest): raise ConfigurationError( "The layer interface must extend `IJSONRPCRequest`.") else: layer = interfaces.IJSONRPCRequest # If there were special permission settings provided, then use them if permission == 'zope.Public': permission = CheckerPublic require = {} for attr_name in methods: require[attr_name] = permission if interface: for iface in interface: for field_name in iface: require[field_name] = permission _context.action(discriminator=None, callable=provideInterface, args=('', for_)) # Make sure that the class inherits MethodPublisher, so that the views # have a location if class_ is None: class_ = original_class = MethodPublisher else: original_class = class_ class_ = type(class_.__name__, (class_, MethodPublisher), {}) if name: # Register a single jsonrpc view if permission: checker = Checker(require) def proxyView(context, request, class_=class_, checker=checker): view = class_(context, request) # We need this in case the resource gets unwrapped and # needs to be rewrapped view.__Security_checker__ = checker return view class_ = proxyView class_.factory = original_class else: # No permission was defined, so we defer to the checker # of the original class def proxyView(context, request, class_=class_): view = class_(context, request) view.__Security_checker__ = getCheckerForInstancesOf( original_class) return view class_ = proxyView class_.factory = original_class # Register the new view. _context.action(discriminator=('jsonrpc', for_, name, layer), callable=handler, args=('registerAdapter', class_, (for_, layer), Interface, name, _context.info)) else: if permission: checker = Checker({'__call__': permission}) else: raise ConfigurationError( "JSONRPC view has neither a name nor a permission. " "You have to specify at least one of the two.") for name in require: # create a new callable class with a security checker; cdict = { '__Security_checker__': checker, '__call__': getattr(class_, name) } new_class = type(class_.__name__, (class_, ), cdict) _context.action(discriminator=('jsonrpc', for_, name, layer), callable=handler, args=('registerAdapter', new_class, (for_, layer), Interface, name, _context.info)) # Register the used interfaces with the site manager if for_ is not None: _context.action(discriminator=None, callable=provideInterface, args=('', for_))
def test_canWrite_canAccess(self): # the canWrite and canAccess functions are conveniences. Often code # wants to check if a certain option is open to a user before # presenting it. If the code relies on a certain permission, the # Zope 3 goal of keeping knowledge of security assertions out of the # code and only in the zcml assertions is broken. Instead, ask if the # current user canAccess or canWrite some pertinent aspect of the # object. canAccess is used for both read access on an attribute # and call access to methods. # For example, consider this humble pair of class and object. class SomeClass(object): pass obj = SomeClass() # We will establish a checker for the class. This is the standard # name-based checker, and works by specifying two dicts, one for read # and one for write. Each item in the dictionary should be an # attribute name and the permission required to read or write it. # For these tests, the SecurityPolicy defined at the top of this file # is in place. It is a stub. Normally, the security policy would # have knowledge of interactions and participants, and would determine # on the basis of the particpants and the object if a certain permission # were authorized. This stub simply says that the 'test_allowed' # permission is authorized and nothing else is, for any object you pass # it. # Therefore, according to the checker created here, the current # 'interaction' (as stubbed out in the security policy) will be allowed # to access and write foo, and access bar. The interaction is # unauthorized for accessing baz and writing bar. Any other access or # write is not merely unauthorized but forbidden--including write access # for baz. checker = Checker( { 'foo': 'test_allowed', # these are the read settings 'bar': 'test_allowed', 'baz': 'you_will_not_have_this_permission' }, { 'foo': 'test_allowed', # these are the write settings 'bar': 'you_will_not_have_this_permission', 'bing': 'you_will_not_have_this_permission' }) defineChecker(SomeClass, checker) # so, our hapless interaction may write and access foo... self.assert_(canWrite(obj, 'foo')) self.assert_(canAccess(obj, 'foo')) # ...may access, but not write, bar... self.assert_(not canWrite(obj, 'bar')) self.assert_(canAccess(obj, 'bar')) # ...and may access baz. self.assert_(not canAccess(obj, 'baz')) # there are no security assertions for writing or reading shazam, so # checking these actually raises Forbidden. The rationale behind # exposing the Forbidden exception is primarily that it is usually # indicative of programming or configuration errors. self.assertRaises(Forbidden, canAccess, obj, 'shazam') self.assertRaises(Forbidden, canWrite, obj, 'shazam') # However, we special-case canWrite when an attribute has a Read # setting but no Write setting. Consider the 'baz' attribute from the # checker above: it is readonly. All users are forbidden to write # it. This is a very reasonable configuration. Therefore, canWrite # will hide the Forbidden exception if and only if there is a # setting for accessing the attribute. self.assert_(not canWrite(obj, 'baz')) # The reverse is not true at the moment: an unusal case like the # write-only 'bing' attribute will return a boolean for canWrite, # but canRead will simply raise a Forbidden exception, without checking # write settings. self.assert_(not canWrite(obj, 'bing')) self.assertRaises(Forbidden, canAccess, obj, 'bing')
except (NotFound,): pass # this is not really an error #debug.log_exc_info(sys.exc_info(), log.debug) except (TypeError, Exception): debug.log_exc_info(sys.exc_info(), log.error) traverser = ItemTraverser(self, request) return traverser.publishTraverse(request, name) # ensure public security setting for these Section attributes from zope.security.checker import CheckerPublic, Checker, defineChecker _PUBLIC_ATTRS = { 'browserDefault':CheckerPublic, '__call__':CheckerPublic, 'publishTraverse':CheckerPublic } defineChecker(Section, Checker(_PUBLIC_ATTRS)) class QueryContent(object): interface.implements(IQueryContent, IDCDescriptiveProperties) def __init__(self, query, title=None, description=None, marker=None): if marker is not None: def query(parent, query=query): obj = query(parent) interface.alsoProvides(obj, marker) return obj self.query = query self.title = title self.description = description
def _makeSecurityProxy(self, obj): from zope.security.proxy import Proxy from zope.security.checker import Checker checker = Checker({}) return Proxy(obj, checker)
# subclass of str or basestring (needed by the technique used by evoque # for managing guaranteed once-and-only-once automatic escaping of all # template input data) is not acceptable for zope. And, adapting the # result object to an IResult does not help as it anyhow requires that # the object is a str in the first place. But, given that this is the # "final" return in the render chain of a page template, thus no # further escaping will be needed, we can anyhow safely cast the # result to unicode. return unicode( super(PageViewTemplateFile, self).__call__(*args, **kwds)) # ensure public security setting for these attributes from zope.security.checker import CheckerPublic, Checker, defineChecker PUBLIC_ATTRS = {'__call__': CheckerPublic} defineChecker(ViewTemplateString, Checker(PUBLIC_ATTRS)) defineChecker(ViewTemplateFile, Checker(PUBLIC_ATTRS)) defineChecker(PageViewTemplateFile, Checker(PUBLIC_ATTRS)) # View Template Helpers class ViewMapper(object): """from: zope.app.pagetemplate.viewpagetemplatefile.ViewMapper""" def __init__(self, ob, request): self.ob = ob self.request = request def __getitem__(self, name): return zope.component.getMultiAdapter((self.ob, self.request), name=name)
def page( _context, name, permission, for_, layer=IDefaultBrowserLayer, template=None, class_=None, allowed_interface=None, allowed_attributes=None, attribute='__call__', menu=None, title=None, ): _handle_menu(_context, menu, title, [for_], name, permission, layer) required = {} permission = _handle_permission(_context, permission) if not (class_ or template): raise ConfigurationError("Must specify a class or template") if attribute != '__call__': if template: raise ConfigurationError( "Attribute and template cannot be used together.") if not class_: raise ConfigurationError( "A class must be provided if attribute is used") if template: template = os.path.abspath(str(_context.path(template))) if not os.path.isfile(template): raise ConfigurationError("No such file", template) required['__getitem__'] = permission # TODO: new __name__ attribute must be tested if class_: if attribute != '__call__': if not hasattr(class_, attribute): raise ConfigurationError( "The provided class doesn't have the specified attribute ") if template: # class and template new_class = SimpleViewClass(template, bases=(class_, ), name=name) else: if not hasattr(class_, 'browserDefault'): cdict = { 'browserDefault': lambda self, request: (getattr(self, attribute), ()) } else: cdict = {} cdict['__name__'] = name cdict['__page_attribute__'] = attribute new_class = type(class_.__name__, ( class_, simple, ), cdict) if hasattr(class_, '__implements__'): classImplements(new_class, IBrowserPublisher) else: # template new_class = SimpleViewClass(template, name=name) for n in (attribute, 'browserDefault', '__call__', 'publishTraverse'): required[n] = permission _handle_allowed_interface(_context, allowed_interface, permission, required) _handle_allowed_attributes(_context, allowed_attributes, permission, required) _handle_for(_context, for_) defineChecker(new_class, Checker(required)) _context.action( discriminator=('view', for_, name, IBrowserRequest, layer), callable=handler, args=('registerAdapter', new_class, (for_, layer), Interface, name, _context.info), )