diff --git a/docs/modules/index.rst b/docs/modules/index.rst index 1cdb36a0..0f67513b 100644 --- a/docs/modules/index.rst +++ b/docs/modules/index.rst @@ -14,4 +14,5 @@ Internal Modules server.rst settings.rst slicing.rst + users.rst util.rst diff --git a/docs/modules/users.rst b/docs/modules/users.rst new file mode 100644 index 00000000..f147a46a --- /dev/null +++ b/docs/modules/users.rst @@ -0,0 +1,9 @@ +.. _sec-modules-users: + +octoprint.users +--------------- + +.. automodule:: octoprint.users + :members: + + diff --git a/docs/plugins/hooks.rst b/docs/plugins/hooks.rst index cb9a5cc8..e1009b41 100644 --- a/docs/plugins/hooks.rst +++ b/docs/plugins/hooks.rst @@ -946,3 +946,38 @@ octoprint.ui.web.templatetypes :param dict template_sorting: read-only dictionary of currently configured template sorting specifications :return: a list of 3-tuples (template type, rule, sorting spec) :rtype: list + +.. _sec-plugins-hook-users-factory: + +octoprint.users.factory +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:function:: user_manager_factory_hook(components, settings, *args, **kwargs) + + Return a :class:`~octoprint.users.UserManager` instance to use as global user manager object. This will + be called only once during initial server startup. + + The provided ``components`` is a dictionary containing the already initialized system components: + + * ``plugin_manager``: The :class:`~octoprint.plugin.core.PluginManager` + * ``printer_profile_manager``: The :class:`~octoprint.printer.profile.PrinterProfileManager` + * ``event_bus``: The :class:`~octoprint.events.EventManager` + * ``analysis_queue``: The :class:`~octoprint.filemanager.analysis.AnalysisQueue` + * ``slicing_manager``: The :class:`~octoprint.slicing.SlicingManager` + * ``file_manager``: The :class:`~octoprint.filemanager.FileManager` + * ``app_session_manager``: The :class:`~octoprint.server.util.flask.AppSessionManager` + * ``plugin_lifecycle_manager``: The :class:`~octoprint.server.LifecycleManager` + * ``preemptive_cache``: The :class:`~octoprint.server.util.flask.PreemptiveCache` + + If the factory returns anything but ``None``, it will be assigned to the global ``userManager`` instance. + + If no of the registered factories return a user manager instance, the class referenced by the ``config.yaml`` + entry ``accessControl.userManager`` will be initialized if possible, otherwise a stock + :class:`~octoprint.users.FilebasedUserManager` will be instantiated, linked to the default user storage + file ``~/.octoprint/users.yaml``. + + :param dict components: System components to use for user manager instance initialization + :param SettingsManager settings: The global settings manager instance to fetch configuration values from if necessary + :return: The ``userManager`` instance to use globally. + :rtype: UserManager subclass or None + diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index d4ef1f79..bc11a1d1 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -284,11 +284,33 @@ class Server(object): file_manager=fileManager, app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, - user_manager=userManager, preemptive_cache=preemptiveCache, connectivity_checker=connectivityChecker ) + # create user manager instance + user_manager_factories = pluginManager.get_hooks("octoprint.users.factory") + for name, factory in user_manager_factories.items(): + try: + userManager = factory(components, self._settings) + if userManager is not None: + self._logger.debug("Created user manager instance from factory {}".format(name)) + break + except: + self._logger.exception("Error while creating user manager instance from factory {}".format(name)) + else: + name = self._settings.get(["accessControl", "userManager"]) + try: + clazz = octoprint.util.get_class(name) + userManager = clazz() + except: + self._logger.exception( + "Could not instantiate user manager {}, falling back to FilebasedUserManager!".format(name)) + userManager = octoprint.users.FilebasedUserManager() + finally: + userManager.enabled = self._settings.getBoolean(["accessControl", "enabled"]) + components.update(dict(user_manager=userManager)) + # create printer instance printer_factories = pluginManager.get_hooks("octoprint.printer.factory") for name, factory in printer_factories.items():