Source code for rules_light.registry

The rule registry is in charge of keeping and executing security rules.

It is the core of this app, everything else is optionnal.

This module provides a variable, ``registry``, which is just a module-level,
default RuleRegistry instance.

A rule can be a callback or a variable that will be evaluated as bool.
from __future__ import unicode_literals

import logging

from django.utils.encoding import smart_text

from .exceptions import Denied, DoesNotExist

__all__ = ('RuleRegistry', 'registry', 'require', 'run', 'autodiscover')

[docs]class RuleRegistry(dict): """ Dict subclass to manage rules. logger The standard logging logger instance to use. """ def __init__(self): self.logger = logging.getLogger('rules_light') def __setitem__(self, key, value): """ Adds a debug-level log on registration. """ super(RuleRegistry, self).__setitem__(key, value) self.logger.debug(u'[rules_light] "%s" registered with: %s' % ( key, self.rule_text_name(value)))
[docs] def run(self, user, name, *args, **kwargs): """ Run a rule, return True if whatever it returns evaluates to True. Also logs calls with the info-level. """ if name not in self: self.logger.error(u'[rules_light] Rule does not exist "%s"' % name) raise DoesNotExist(name) rule = self[name] if hasattr(rule, '__call__'): result = self[name](user, name, *args, **kwargs) else: result = rule text = self.as_text(user, name, *args, **kwargs) if result:'[rules_light] %s passed' % text) return True else:'[rules_light] %s failed' % text) return False
[docs] def require(self, user, name, *args, **kwargs): """ Run a rule, raise ``rules_light.Denied`` if returned False. Log denials with warn-level. """ result =, name, *args, **kwargs) if not result: text = self.as_text(user, name, *args, **kwargs) self.logger.warn(u'[rules_light] Deny %s' % text) raise Denied(text)
[docs] def as_text(self, user, name, *args, **kwargs): """ Format a rule to be human readable for logging """ if name not in self: raise DoesNotExist(name) formated_args = [] for arg in args: formated_args.append(u'"%s"' % smart_text(arg)) for key, value in kwargs.items(): formated_args.append(u'%s="%s"' % (smart_text(key), smart_text(value))) formated_args = u', '.join(formated_args) if hasattr(self[name], '__call__'): text_name = self.rule_text_name(self[name]) if formated_args: return u'%s(%s, "%s", %s)' % (text_name, user, name, formated_args) else: return u'%s(%s, "%s")' % (text_name, user, name) else: return u'%s is %s' % (name, self[name])
def rule_text_name(self, rule): if hasattr(rule, 'func_name'): return rule.func_name elif rule is True: return u'True' elif rule is False: return u'False' elif hasattr(rule, '__name__'): return rule.__name__ elif hasattr(rule, '__class__'): return rule.__class__.__name__ else: return smart_text(rule)
registry = RuleRegistry()
[docs]def run(user, name, *args, **kwargs): """ Proxy ````. """ return, name, *args, **kwargs)
[docs]def require(user, name, *args, **kwargs): """ Proxy ``rules_light.registry.require()``. """ registry.require(user, name, *args, **kwargs)
def _autodiscover(registry): """See documentation for autodiscover (without the underscore)""" import copy from django.conf import settings from django.utils.importlib import import_module from django.utils.module_loading import module_has_submodule for app in settings.INSTALLED_APPS: mod = import_module(app) # Attempt to import the app's admin module. try: before_import_registry = copy.copy(registry) import_module('%s.rules_light_registry' % app) except: # Reset the model registry to the state before the last import as # this import will have to reoccur on the next request and this # could raise NotRegistered and AlreadyRegistered exceptions # (see #8245). registry = before_import_registry # Decide whether to bubble up this error. If the app just # doesn't have an admin module, we can ignore the error # attempting to import it, otherwise we want it to bubble up. if module_has_submodule(mod, 'rules_light_registry'): raise
[docs]def autodiscover(): """ Check all apps in INSTALLED_APPS for stuff related to rules_light. For each app, autodiscover imports ``app.rules_light_registry`` if available, resulting in execution of ``rules_light.registry[...] = ...`` statements in that module, filling registry. Consider a standard app called 'cities_light' with such a structure:: cities_light/ With such a import rules_light rules_light.register('', True) rules_light.register('', lambda user, rulename, country: user.is_staff) When autodiscover() imports cities_light.rules_light_registry, both `''` and `''` will be registered. """ _autodiscover(registry)