refactor plugin loading

This commit is contained in:
Matthias 2021-11-12 02:26:10 +01:00
parent 175b6ca053
commit 0e6f203660
No known key found for this signature in database
GPG Key ID: F50EF5741D33E076
2 changed files with 87 additions and 74 deletions

View File

@ -262,7 +262,7 @@ INSTALLED_APPS = [
'report.apps.ReportConfig',
'stock.apps.StockConfig',
'users.apps.UsersConfig',
'plugin.apps.PluginConfig',
'plugin.apps.PluginAppConfig',
'InvenTree.apps.InvenTreeConfig', # InvenTree app runs last
# Third part add-ons

View File

@ -20,55 +20,23 @@ from plugin.integration import IntegrationPluginBase
logger = logging.getLogger('inventree')
class PluginConfig(AppConfig):
class PluginAppConfig(AppConfig):
name = 'plugin'
def ready(self):
from common.models import InvenTreeSetting
from plugin.models import PluginConfig
# Collect plugins from paths
for plugin in settings.PLUGIN_DIRS:
modules = inventree_plugins.get_plugins(importlib.import_module(plugin), IntegrationPluginBase, True)
if modules:
[settings.PLUGINS.append(item) for item in modules]
# Collect plugins from setup entry points
for entry in metadata.entry_points().get('inventree_plugins', []):
plugin = entry.load()
plugin.is_package = True
settings.PLUGINS.append(plugin)
# Log found plugins
logger.info(f'Found {len(settings.PLUGINS)} plugins!')
logger.info(", ".join([a.__module__ for a in settings.PLUGINS]))
self.collect_plugins()
try:
logger.info('Starting plugin initialisation')
# Initialize integration plugins
for plugin in inventree_plugins.load_integration_plugins():
# check if package
was_packaged = getattr(plugin, 'is_package', False)
# we are using the db from here - so for migrations etc we need to try this block
self.init_plugins()
self.activate_plugins()
except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet
pass
# check if activated
# these checks only use attributes - never use plugin supplied functions -> that would lead to arbitrary code execution!!
plug_name = plugin.PLUGIN_NAME
plug_key = plugin.PLUGIN_SLUG if getattr(plugin, 'PLUGIN_SLUG', None) else plug_name
plugin_db_setting, _ = PluginConfig.objects.get_or_create(key=plug_key, name=plug_name)
if plugin_db_setting.active:
# init package
# now we can be sure that an admin has activated the plugin -> as of Nov 2021 there are not many checks in place
# but we could enhance those to check signatures, run the plugin against a whitelist etc.
logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}')
plugin = plugin()
logger.info(f'Loaded integration plugin {plugin.slug}')
plugin.is_package = was_packaged
# safe reference
settings.INTEGRATION_PLUGINS[plugin.slug] = plugin
else:
# save for later reference
settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting
def activate_plugins(self):
"""fullfill integrations for all activated plugins"""
from common.models import InvenTreeSetting
# activate integrations
plugins = settings.INTEGRATION_PLUGINS.items()
@ -111,6 +79,51 @@ class PluginConfig(AppConfig):
apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
apps.clear_cache()
apps.populate(settings.INSTALLED_APPS)
except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet
pass
def init_plugins(self):
"""initialise all found plugins"""
from plugin.models import PluginConfig
logger.info('Starting plugin initialisation')
# Initialize integration plugins
for plugin in inventree_plugins.load_integration_plugins():
# check if package
was_packaged = getattr(plugin, 'is_package', False)
# check if activated
# these checks only use attributes - never use plugin supplied functions -> that would lead to arbitrary code execution!!
plug_name = plugin.PLUGIN_NAME
plug_key = plugin.PLUGIN_SLUG if getattr(plugin, 'PLUGIN_SLUG', None) else plug_name
plugin_db_setting, _ = PluginConfig.objects.get_or_create(key=plug_key, name=plug_name)
if plugin_db_setting.active:
# init package
# now we can be sure that an admin has activated the plugin -> as of Nov 2021 there are not many checks in place
# but we could enhance those to check signatures, run the plugin against a whitelist etc.
logger.info(f'Loading integration plugin {plugin.PLUGIN_NAME}')
plugin = plugin()
logger.info(f'Loaded integration plugin {plugin.slug}')
plugin.is_package = was_packaged
# safe reference
settings.INTEGRATION_PLUGINS[plugin.slug] = plugin
else:
# save for later reference
settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting
def collect_plugins(self):
"""collect integration plugins from all possible ways of loading"""
# Collect plugins from paths
for plugin in settings.PLUGIN_DIRS:
modules = inventree_plugins.get_plugins(importlib.import_module(plugin), IntegrationPluginBase, True)
if modules:
[settings.PLUGINS.append(item) for item in modules]
# Collect plugins from setup entry points
for entry in metadata.entry_points().get('inventree_plugins', []):
plugin = entry.load()
plugin.is_package = True
settings.PLUGINS.append(plugin)
# Log found plugins
logger.info(f'Found {len(settings.PLUGINS)} plugins!')
logger.info(", ".join([a.__module__ for a in settings.PLUGINS]))