authentic/src/authentic2/hooks.py

75 lines
2.6 KiB
Python

# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from django.apps import apps
from django.conf import settings
from . import decorators
@decorators.GlobalCache
def get_hooks(hook_name):
"""Return a list of defined hook named a2_hook<hook_name> on AppConfig classes of installed
Django applications.
Ordering of hooks can be defined using an orer field on the hook method.
"""
hooks = []
for app in apps.get_app_configs():
name = 'a2_hook_' + hook_name
if hasattr(app, name):
hooks.append(getattr(app, name))
if hasattr(settings, 'A2_HOOKS') and hasattr(settings.A2_HOOKS, 'items'):
v = settings.A2_HOOKS.get(hook_name)
if callable(v):
hooks.append(v)
v = settings.A2_HOOKS.get('__all__')
if callable(v):
hooks.append(lambda *args, **kwargs: v(hook_name, *args, **kwargs))
hooks.sort(key=lambda hook: getattr(hook, 'order', 0))
return hooks
@decorators.to_list
def call_hooks(hook_name, *args, **kwargs):
'''Call each a2_hook_<hook_name> and return the list of results.'''
logger = logging.getLogger(__name__)
hooks = get_hooks(hook_name)
for hook in hooks:
try:
yield hook(*args, **kwargs)
except Exception:
if getattr(settings, 'A2_HOOKS_PROPAGATE_EXCEPTIONS', False):
raise
logger.exception('exception while calling hook %s', hook)
def call_hooks_first_result(hook_name, *args, **kwargs):
'''Call each a2_hook_<hook_name> and return the first not None result.'''
logger = logging.getLogger(__name__)
hooks = get_hooks(hook_name)
for hook in hooks:
try:
result = hook(*args, **kwargs)
if result is not None:
return result
except Exception:
if getattr(settings, 'A2_HOOKS_PROPAGATE_EXCEPTIONS', False):
raise
logger.exception('exception while calling hook %s', hook)