def fetch_settings(cls): LodelContext.expose_dyncode(globals(), 'dyncode') if cls._infos_fields is None: cls._infos_fields = list() else: # Already fetched return infos = (Settings.auth.login_classfield, Settings.auth.pass_classfield) res_infos = [] for clsname, fieldname in infos: dcls = dyncode.lowername2class(infos[0][0]) res_infos.append((dcls, infos[1][1])) link_field = None if res_infos[0][0] != res_infos[1][0]: # login and password are in two separated EmClass # determining the field that links login EmClass to password # EmClass for fname, fdh in res_infos[0][0].fields(True).items(): if fdh.is_reference( ) and res_infos[1][0] in fdh.linked_classes(): link_field = fname if link_field is None: # Unable to find link between login & password EmClass raise AuthenticationError("Unable to find a link between \ login EmClass '%s' and password EmClass '%s'. Abording..." % (res_infos[0][0], res_infos[1][0])) res_infos[0] = (res_infos[0][0], res_infos[0][1], link_field) cls._infos_fields.append({ 'login': res_infos[0], 'password': res_infos[1] })
def plugin_name(ds_name, ro): LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) # fetching connection identifier given datasource name try: ds_identifier = getattr(Settings.datasources, ds_name) except (NameError, AttributeError): raise DatasourcePluginError("Unknown or unconfigured datasource \ '%s'" % ds_name) # fetching read_only flag try: read_only = getattr(ds_identifier, 'read_only') except (NameError, AttributeError): raise SettingsError("Malformed datasource configuration for '%s' \ : missing read_only key" % ds_name) # fetching datasource identifier try: ds_identifier = getattr(ds_identifier, 'identifier') except (NameError, AttributeError) as e: raise SettingsError("Malformed datasource configuration for '%s' \ : missing identifier key" % ds_name) # settings and ro arg consistency check if read_only and not ro: raise DatasourcePluginError("ro argument was set to False but \ True found in settings for datasource '%s'" % ds_name) res = ds_identifier.split('.') if len(res) != 2: raise SettingsError("expected value for identifier is like \ DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier) return res
def plugin_validator(value, ptype=None): if value: LodelContext.expose_modules(globals(), {'lodel.plugin.hooks': ['LodelHook']}) value = copy.copy(value) @LodelHook('lodel2_dyncode_bootstraped') def plugin_type_checker(hookname, caller, payload): LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError'] }) if value is None: return try: plugin = Plugin.get(value) except PluginError: msg = "No plugin named %s found" msg %= value raise ValidationError(msg) if plugin._type_conf_name.lower() != ptype.lower(): msg = "A plugin of type '%s' was expected but found a plugin \ named '%s' that is a '%s' plugin" msg %= (ptype, value, plugin._type_conf_name) raise ValidationError(msg) return value return None
def plugin_list_confspec(cls): LodelContext.expose_modules(globals(), { 'lodel.settings.validator': ['confspec_append']}) res = dict() for pcls in cls.plugin_types(): plcs = pcls.plist_confspec() confspec_append(res, plcs) return res
def call_hook(cls, hook_name, caller, payload): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) logger.debug("Calling hook '%s'" % hook_name) if hook_name in cls._hooks: for hook in cls._hooks[hook_name]: logger.debug("Lodel hook '%s' calls %s" % (hook_name, hook)) payload = hook(hook_name, caller, payload) return payload
def start(): #Load plugins LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.plugin': ['Plugin']}) logger.debug("Loader.start() called") Plugin.load_all() LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
def log(self): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) msg = "Webui HTTP exception : %s" % self if self.status_code / 100 == 4: logger.security(msg) elif self.status_code / 100 == 5: logger.error(msg) else: logger.warning(msg)
def __bootstrap(self): LodelContext.expose_modules( globals(), {'lodel.plugin.plugins': ['Plugin', 'PluginError']}) logger.debug("Settings bootstraping") if self.__conf_specs is None: lodel2_specs = LODEL2_CONF_SPECS else: lodel2_specs = self.__conf_specs self.__conf_specs = None loader = SettingsLoader(self.__conf_dir) plugin_list = [] for ptype_name, ptype in Plugin.plugin_types().items(): pls = ptype.plist_confspecs() lodel2_specs = confspec_append(lodel2_specs, **pls) cur_list = loader.getoption(pls['section'], pls['key'], pls['validator'], pls['default']) if cur_list is None: continue try: if isinstance(cur_list, str): cur_list = [cur_list] plugin_list += cur_list except TypeError: plugin_list += [cur_list] # Remove invalid plugin names plugin_list = [plugin for plugin in plugin_list if len(plugin) > 0] # Checking confspecs for section in lodel2_specs: if section.lower() != section: raise SettingsError( "Only lower case are allowed in section name (thank's ConfigParser...)" ) for kname in lodel2_specs[section]: if kname.lower() != kname: raise SettingsError( "Only lower case are allowed in section name (thank's ConfigParser...)" ) # Starting the Plugins class logger.debug("Starting lodel.plugin.Plugin class") Plugin.start(plugin_list) # Fetching conf specs from plugins specs = [lodel2_specs] errors = list() for plugin_name in plugin_list: try: specs.append(Plugin.get(plugin_name).confspecs) except PluginError as e: errors.append(SettingsError(msg=str(e))) if len(errors) > 0: # Raise all plugins import errors raise SettingsErrors(errors) self.__conf_specs = self.__merge_specs(specs) self.__populate_from_specs(self.__conf_specs, loader) self.__started = True
def list_hook_debug_hook(name, caller, payload): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) hlist = LodelHook.hook_list() for name, reg_hooks in hlist.items(): for hook, priority in reg_hooks: logger.debug("{modname}.{funname} is registered as hook \ {hookname} with priority {priority}".format(modname=hook.__module__, funname=hook.__name__, priority=priority, hookname=name))
def _get_ds_connection_conf(ds_identifier, ds_plugin_name): LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) if ds_plugin_name not in Settings.datasource._fields: msg = "Unknown or unconfigured datasource plugin %s" msg %= ds_plugin_name raise DatasourcePluginError(msg) ds_conf = getattr(Settings.datasource, ds_plugin_name) if ds_identifier not in ds_conf._fields: msg = "Unknown or unconfigured datasource instance %s" msg %= ds_identifier raise DatasourcePluginError(msg) ds_conf = getattr(ds_conf, ds_identifier) return {k: getattr(ds_conf, k) for k in ds_conf._fields}
def add_handler(name, logging_opt): logger = logging.getLogger(LodelContext.get_name()) if name in handlers: raise KeyError("A handler named '%s' allready exists") logging_opt = logging_opt._asdict() if 'filename' in logging_opt and logging_opt['filename'] is not None: maxBytes = (1024 * 10) if 'maxbytes' not in logging_opt else logging_opt['maxbytes'] backupCount = 10 if 'backupcount' not in logging_opt else logging_opt['backupcount'] handler = logging.handlers.RotatingFileHandler( logging_opt['filename'], maxBytes = maxBytes, backupCount = backupCount, encoding = 'utf-8') else: handler = logging.StreamHandler() if 'level' in logging_opt: handler.setLevel(getattr(logging, logging_opt['level'].upper())) if 'format' in logging_opt: formatter = logging.Formatter(logging_opt['format']) else: if 'context' in logging_opt and not logging_opt['context']: formatter = logging.Formatter(simple_format) else: formatter = logging.Formatter(default_format) handler.setFormatter(formatter) handlers[name] = handler logger.addHandler(handler)
def application(env, start_response): #Attempt to load a context site_id = site_id_from_url(env['PATH_INFO']) if site_id is None: #It can be nice to provide a list of instances here return http_error(env, start_response, '404 Not Found') try: LodelContext.set(site_id) #We are in the good context except ContextError as e: print(e) return http_error(env, start_response, '404 Not found', "No site named '%s'" % site_id) #Calling webui return FAST_APP_EXPOSAL_CACHE[site_id].application(env, start_response)
def dyncode_from_em(model): # Generation of LeObject child classes code cls_code, bootstrap_instr = generate_classes(model) # Header imports = """from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.leapi.leobject': ['LeObject'], 'lodel.leapi.datahandlers.base_classes': ['DataField'], 'lodel.plugin.hooks': ['LodelHook']}) """ # generates the list of all classes in the editorial model class_list = [LeObject.name2objname(cls.uid) for cls in get_classes(model)] # formating all components of output res_code = """#-*- coding: utf-8 -*- {imports} {classes} {bootstrap_instr} #List of dynamically generated classes dynclasses = {class_list} #Dict of dynamically generated classes indexed by name dynclasses_dict = {class_dict} {common_code} """.format( imports=imports, classes=cls_code, bootstrap_instr=bootstrap_instr, class_list='[' + (', '.join([cls for cls in class_list])) + ']', class_dict='{' + (', '.join(["'%s': %s" % (cls, cls) for cls in class_list])) + '}', common_code=common_code(), ) return res_code
def __init_from_settings(): from lodel.context import LodelContext try: LodelContext.expose_modules(globals(), { 'lodel.settings': ['Settings']}) except Exception: return False LodelContext.expose_modules(globals(), { 'lodel.settings.settings': [('Settings', 'Lodel2Settings')]}) if not Lodel2Settings.started(): return False # capture warning disabled, because the custom format raises error (unable # to give the _ preffixed arguments to logger resulting into a KeyError # exception ) #logging.captureWarnings(True) # Log warnings logger.setLevel(logging.DEBUG) for name in Settings.logging._fields: add_handler(name, getattr(Settings.logging, name)) return True
def log(lvl, msg, *args, **kwargs): if len(handlers) == 0: #late initialisation if not __init_from_settings(): s_kwargs = copy.copy(kwargs) s_kwargs.update({'lvl': lvl, 'msg':msg}) msg_buffer.append((s_kwargs, args)) return else: for s_kwargs, args in msg_buffer: log(*args, **s_kwargs) from lodel.context import LodelContext if LodelContext.multisite(): msg = "CTX(%s) %s" % (LodelContext.get_name(), msg) caller = logger.findCaller() # Opti warning : small overhead extra = { '_pathname': os.path.abspath(caller[0]), '_lineno': caller[1], '_funcName': caller[2], } logger.log(lvl, msg, extra = extra, *args, **kwargs)
def plugin_type_checker(hookname, caller, payload): LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError'] }) if value is None: return try: plugin = Plugin.get(value) except PluginError: msg = "No plugin named %s found" msg %= value raise ValidationError(msg) if plugin._type_conf_name.lower() != ptype.lower(): msg = "A plugin of type '%s' was expected but found a plugin \ named '%s' that is a '%s' plugin" msg %= (ptype, value, plugin._type_conf_name) raise ValidationError(msg)
def _discover(cls, path): # Ensure plugins symlink creation LodelContext.expose_modules(globals(), { 'lodel.plugins': 'plugins'}) res = [] to_explore = [path] while len(to_explore) > 0: cur_path = to_explore.pop() for f in os.listdir(cur_path): f_path = os.path.join(cur_path, f) if f not in ['.', '..'] and os.path.isdir(f_path): # Check if it is a plugin directory test_result = cls.dir_is_plugin(f_path) if not (test_result is False): logger.info("Plugin '%s' found in %s" % ( test_result['name'], f_path)) res.append(test_result) else: to_explore.append(f_path) return res
def emfield_val(value): LodelContext.expose_modules(globals(), {'lodel.plugin.hooks': ['LodelHook']}) spl = value.split('.') if len(spl) != 2: msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s" raise ValidationError(msg % value) value = tuple(spl) # Late validation hook @LodelHook('lodel2_dyncode_bootstraped') def emfield_conf_check(hookname, caller, payload): import leapi_dyncode as dyncode # <-- dirty & quick classnames = {cls.__name__.lower(): cls for cls in dyncode.dynclasses} if value[0].lower() not in classnames: msg = "Following dynamic class do not exists in current EM : %s" raise ValidationError(msg % value[0]) ccls = classnames[value[0].lower()] if value[1].lower() not in ccls.fieldnames(True): msg = "Following field not found in class %s : %s" raise ValidationError(msg % value) return value
# 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 sys import os.path import re import socket import inspect import copy from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'FieldValidationError' ] }) ## # @package lodel.settings.validator Lodel2 settings validators/cast module. # # Validator are registered in the Validator class. # @note to get a list of registered default validators just run # <pre>$ python scripts/settings_validator.py</pre> # # @remarks Should we reconsider specifying conf right in this module? ##
# # 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/>. # ## @package lodel.plugins.dummy.main Plugin's loader module from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.plugin': ['LodelHook', 'CustomMethod'], 'lodel.settings': 'settings' }) ## @brief callback method using lodel's hook system # @param hook_name str # @param caller function : action to perform # @param payload : data to pass to the caller # @return payload @LodelHook('leapi_get_post') @LodelHook('leapi_update_pre') @LodelHook('leapi_update_post') @LodelHook('leapi_delete_pre') @LodelHook('leapi_delete_post') @LodelHook('leapi_insert_pre') @LodelHook('leapi_insert_post')
import copy import logging, logging.handlers import os.path # Variables & constants definitions default_format = '%(asctime)-15s %(levelname)s %(_pathname)s:%(_lineno)s:%(_funcName)s() : %(message)s' simple_format = '%(asctime)-15s %(levelname)s : %(message)s' SECURITY_LOGLEVEL = 35 logging.addLevelName(SECURITY_LOGLEVEL, 'SECURITY') handlers = dict() # Handlers list (generated from settings) ##@brief Stores sent messages until module is able to be initialized msg_buffer = [] # Fetching logger for current context from lodel.context import LodelContext logger = logging.getLogger(LodelContext.get_name()) ##@brief Module initialisation from settings #@return True if inited else False def __init_from_settings(): from lodel.context import LodelContext try: LodelContext.expose_modules(globals(), { 'lodel.settings': ['Settings']}) except Exception: return False LodelContext.expose_modules(globals(), { 'lodel.settings.settings': [('Settings', 'Lodel2Settings')]}) if not Lodel2Settings.started(): return False # capture warning disabled, because the custom format raises error (unable
# In this class, there is the MongoDbDatasource class, that handles the basic # operations that can be done (CRUD ones). import re import warnings import copy import functools from bson.son import SON from collections import OrderedDict import pymongo from pymongo.errors import BulkWriteError from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME'], 'lodel.leapi.datahandlers.base_classes': ['Reference', 'MultipleRef'], 'lodel.exceptions': ['LodelException', 'LodelFatalError'], 'lodel.plugin.datasource_plugin': ['AbstractDatasource']}) from . import utils from .exceptions import * from .utils import object_collection_name, collection_name, \ MONGODB_SORT_OPERATORS_MAP, connection_string, mongo_fieldname ## @brief Datasource class # @ingroup plugin_mongodb_datasource class MongoDbDatasource(AbstractDatasource): ## @brief Stores existing connections #
# # 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 jinja2 import os from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) LodelContext.expose_dyncode(globals()) from ...client import WebUiClient as WebUiClient from .api import api_lodel_templates from .exceptions.not_allowed_custom_api_key_error import NotAllowedCustomAPIKeyError from ...main import root_url as root_url from ...main import static_url as static_url from ...main import PLUGIN_PATH TEMPLATE_PATH = os.path.realpath(os.path.join(PLUGIN_PATH, 'templates/')) class TemplateLoader(object):
# 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 datetime from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.editorial_model.components': ['EmClass', 'EmField'], 'lodel.editorial_model.model': ['EditorialModel'], 'lodel.leapi.datahandlers.base_classes': ['DataHandler'], 'lodel.plugin': ['LodelHook'], 'lodel.logger': 'logger' }) from leapi_dyncode import * #<-- TODO : handle this !!! from .utils import connect, object_collection_name, mongo_fieldname from .datasource import MongoDbDatasource from .exceptions import * class MigrationHandler(object): ## @brief Constructs a MongoDbMigrationHandler # @param host str
# 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/>. # # @package lodel.auth.exceptions # @brief Defines the specific exceptions used in the authentication process from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.plugin.hooks': ['LodelHook']}) ## @brief Handles common errors with a Client class ClientError(Exception): ## @brief The logger function to use to log the error message _loglvl = 'warning' ## @brief Error string _err_str = "Error" ## @brief the hook name to trigger with error _action = 'lodel2_ui_error' ## @brief the hook payload _payload = None ## @brief Constructor
# This file is part of Lodel 2 (https://github.com/OpenEdition) # # Copyright (C) 2015-2017 Cléo UMS-3287 # # 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/>. # ## @package lodel.plugins.dummy_datasource.main The main module of the plugin. from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.plugin': ['LodelHook']}) from .datasource import DummyDatasource as Datasource ## @brief returns the migration handler of this plugin. # @retunr DummyMigrationHandler def migration_handler_class(): from .migration_handler import DummyMigrationHandler as migration_handler return migration_handler
# ## @package lodel.leapi.datahandlers.datas # This module contains specific datahandlers extending the basic ones from the lodel.leapi.datahandlers.datas_base module. import warnings import inspect import re from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar', 'DateTime', 'Text', 'File'], 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError' ] }) ## @brief Data field designed to handle formated strings class FormatString(Varchar): help = 'Automatic string field, designed to use the str % operator to build its content' base_type = 'char' ## @brief Constructor # @param _field_list list : List of fields to use # @param _format_string str : formatted string
# # 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 sys import os, os.path sys.path.append(os.path.dirname(os.getcwd() + '/..')) from lodel.context import LodelContext LodelContext.init() from lodel.settings.settings import Settings as settings settings('globconf.d') from lodel.settings import Settings def generate_dyncode(model_file, translator): from lodel.editorial_model.model import EditorialModel from lodel.leapi import lefactory model = EditorialModel.load(translator, filename=model_file) dyncode = lefactory.dyncode_from_em(model) return dyncode def refresh_dyncode(model_file, translator, output_filename):
# 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/>. # from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.validator.validator': ['Validator']}) #Define a minimal confspec used by multisite loader LODEL2_CONFSPECS = { 'lodel2': { 'debug': (True, Validator('bool')) }, 'lodel2.server': { 'listen_address': ('127.0.0.1', Validator('dummy')), #'listen_address': ('', Validator('ip')), #<-- not implemented 'listen_port': (1337, Validator('int')), 'uwsgi_workers': (8, Validator('int')), 'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')), 'virtualenv': (None, Validator('path', none_is_valid=True)), }, 'lodel2.logging.*': {
# @package lodel.editorial_model.components #@brief Defines all @ref lodel2_em "EM" components #@ingroup lodel2_em import itertools import warnings import copy import hashlib from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.utils.mlstring': ['MlString'], 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.settings': ['Settings'], 'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'], 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME'] }) ## @brief Abstract class to represent editorial model components # @see EmClass EmField # @todo forbid '.' in uid #@ingroup lodel2_em class EmComponent(MlNamedObject): ## @brief Instanciate an EmComponent # @param uid str : uniq identifier