diff --git a/src/common/core/jobs/jobs/download-plugins.py b/src/common/core/jobs/jobs/download-plugins.py
index 27ca3744d..e15df4f59 100644
--- a/src/common/core/jobs/jobs/download-plugins.py
+++ b/src/common/core/jobs/jobs/download-plugins.py
@@ -1,14 +1,14 @@
#!/usr/bin/python3
from io import BytesIO
-from os import getenv, listdir, makedirs, chmod, stat, _exit
-from os.path import isfile, dirname
+from os import getenv, listdir, makedirs, chmod, stat, _exit, walk
+from os.path import join, isfile, dirname
from stat import S_IEXEC
from sys import exit as sys_exit, path as sys_path
from uuid import uuid4
from glob import glob
from json import load, loads
-from shutil import copytree, rmtree
+from shutil import chown, copytree, rmtree
from traceback import format_exc
from zipfile import ZipFile
@@ -97,6 +97,7 @@ try:
continue
external_plugins = []
+ external_plugins_ids = []
for plugin in listdir("/etc/bunkerweb/plugins"):
with open(
f"/etc/bunkerweb/plugins/{plugin}/plugin.json",
@@ -105,6 +106,18 @@ try:
plugin_file = load(f)
external_plugins.append(plugin_file)
+ external_plugins_ids.append(plugin_file["id"])
+
+ db_plugins = db.get_plugins()
+ for plugin in db_plugins:
+ if plugin["external"] is True and plugin["id"] not in external_plugins_ids:
+ external_plugins.append(plugin)
+
+ # Fix permissions for the certificates
+ for root, dirs, files in walk("/data/plugins", topdown=False):
+ for name in files + dirs:
+ chown(join(root, name), "root", 101)
+ chmod(join(root, name), 0o770)
if external_plugins:
err = db.update_external_plugins(external_plugins)
diff --git a/src/common/db/Database.py b/src/common/db/Database.py
index 4e40cefe9..859d597cd 100644
--- a/src/common/db/Database.py
+++ b/src/common/db/Database.py
@@ -1111,10 +1111,14 @@ class Database:
Jobs.name == job["name"]
).update(updates)
- if exists(f"/usr/share/bunkerweb/core/{plugin['id']}/ui"):
- if {"template.html", "actions.py"}.issubset(
- listdir(f"/usr/share/bunkerweb/core/{plugin['id']}/ui")
- ):
+ path_ui = (
+ f"/var/tmp/bunkerweb/ui/{plugin['id']}/ui"
+ if exists(f"/var/tmp/bunkerweb/ui/{plugin['id']}/ui")
+ else f"/etc/bunkerweb/plugins/{plugin['id']}/ui"
+ )
+
+ if exists(path_ui):
+ if {"template.html", "actions.py"}.issubset(listdir(path_ui)):
db_plugin_page = (
session.query(Plugin_pages)
.with_entities(
@@ -1127,12 +1131,12 @@ class Database:
if db_plugin_page is None:
with open(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html",
+ f"{path_ui}/template.html",
"r",
) as file:
template = file.read().encode("utf-8")
with open(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py",
+ f"{path_ui}/actions.py",
"r",
) as file:
actions = file.read().encode("utf-8")
@@ -1149,18 +1153,16 @@ class Database:
else: # TODO test this
updates = {}
template_checksum = file_hash(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html"
- )
- actions_checksum = file_hash(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py"
+ f"{path_ui}/template.html"
)
+ actions_checksum = file_hash(f"{path_ui}/actions.py")
if (
template_checksum
!= db_plugin_page.template_checksum
):
with open(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html",
+ f"{path_ui}/template.html",
"r",
) as file:
updates.update(
@@ -1174,7 +1176,7 @@ class Database:
if actions_checksum != db_plugin_page.actions_checksum:
with open(
- f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py",
+ f"{path_ui}/actions.py",
"r",
) as file:
updates.update(
@@ -1258,6 +1260,79 @@ class Database:
return ""
+ def get_plugins(self) -> List[Dict[str, Any]]:
+ """Get plugins."""
+ plugins = []
+ with self.__db_session() as session:
+ for plugin in (
+ session.query(Plugins)
+ .with_entities(
+ Plugins.id,
+ Plugins.order,
+ Plugins.name,
+ Plugins.description,
+ Plugins.version,
+ Plugins.external,
+ )
+ .order_by(Plugins.order)
+ .all()
+ ):
+ page = (
+ session.query(Plugin_pages)
+ .with_entities(Plugin_pages.id)
+ .filter_by(plugin_id=plugin.id)
+ .first()
+ )
+ data = {
+ "id": plugin.id,
+ "order": plugin.order,
+ "name": plugin.name,
+ "description": plugin.description,
+ "version": plugin.version,
+ "external": plugin.external,
+ "page": page is not None,
+ "settings": {},
+ }
+
+ for setting in (
+ session.query(Settings)
+ .with_entities(
+ Settings.id,
+ Settings.context,
+ Settings.default,
+ Settings.help,
+ Settings.name,
+ Settings.label,
+ Settings.regex,
+ Settings.type,
+ Settings.multiple,
+ )
+ .filter_by(plugin_id=plugin.id)
+ .all()
+ ):
+ data["settings"][setting.id] = {
+ "context": setting.context,
+ "default": setting.default,
+ "help": setting.help,
+ "id": setting.name,
+ "label": setting.label,
+ "regex": setting.regex,
+ "type": setting.type,
+ } | ({"multiple": setting.multiple} if setting.multiple else {})
+
+ if setting.type == "select":
+ data["settings"][setting.id]["select"] = [
+ select.value
+ for select in session.query(Selects)
+ .with_entities(Selects.value)
+ .filter_by(setting_id=setting.id)
+ .all()
+ ]
+
+ plugins.append(data)
+
+ return plugins
+
def get_plugins_errors(self) -> int:
"""Get plugins errors."""
with self.__db_session() as session:
@@ -1381,3 +1456,33 @@ class Database:
.all()
)
]
+
+ def get_plugin_actions(self, plugin: str) -> Optional[Any]:
+ """get actions file for the plugin"""
+ with self.__db_session() as session:
+ page = (
+ session.query(Plugin_pages)
+ .with_entities(Plugin_pages.actions_file)
+ .filter_by(plugin_id=plugin)
+ .first()
+ )
+
+ if page is None:
+ return None
+
+ return page.actions_file
+
+ def get_plugin_template(self, plugin: str) -> Optional[Any]:
+ """get template file for the plugin"""
+ with self.__db_session() as session:
+ page = (
+ session.query(Plugin_pages)
+ .with_entities(Plugin_pages.template_file)
+ .filter_by(plugin_id=plugin)
+ .first()
+ )
+
+ if page is None:
+ return None
+
+ return page.template_file
diff --git a/src/common/db/model.py b/src/common/db/model.py
index 4d4581c5a..83ba6ab05 100644
--- a/src/common/db/model.py
+++ b/src/common/db/model.py
@@ -61,12 +61,10 @@ class Plugins(Base):
external = Column(Boolean, default=False, nullable=False)
settings = relationship(
- "Settings", back_populates="plugin", cascade="all, delete, delete-orphan"
+ "Settings", back_populates="plugin", cascade="all, delete-orphan"
)
- jobs = relationship(
- "Jobs", back_populates="plugin", cascade="all, delete, delete-orphan"
- )
- pages = relationship("Plugin_pages", back_populates="plugin", cascade="all, delete")
+ jobs = relationship("Jobs", back_populates="plugin", cascade="all, delete-orphan")
+ pages = relationship("Plugin_pages", back_populates="plugin", cascade="all")
class Settings(Base):
@@ -81,7 +79,7 @@ class Settings(Base):
name = Column(String(256), primary_key=True)
plugin_id = Column(
String(64),
- ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
nullable=False,
)
context = Column(CONTEXTS_ENUM, nullable=False)
@@ -92,12 +90,12 @@ class Settings(Base):
type = Column(SETTINGS_TYPES_ENUM, nullable=False)
multiple = Column(String(128), nullable=True)
- selects = relationship("Selects", back_populates="setting", cascade="all, delete")
+ selects = relationship("Selects", back_populates="setting", cascade="all")
services = relationship(
- "Services_settings", back_populates="setting", cascade="all, delete"
+ "Services_settings", back_populates="setting", cascade="all"
)
global_value = relationship(
- "Global_values", back_populates="setting", cascade="all, delete"
+ "Global_values", back_populates="setting", cascade="all"
)
plugin = relationship("Plugins", back_populates="settings")
@@ -107,7 +105,7 @@ class Global_values(Base):
setting_id = Column(
String(256),
- ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
value = Column(String(4096), nullable=False)
@@ -124,14 +122,12 @@ class Services(Base):
method = Column(METHODS_ENUM, nullable=False)
settings = relationship(
- "Services_settings", back_populates="service", cascade="all, delete"
+ "Services_settings", back_populates="service", cascade="all"
)
custom_configs = relationship(
- "Custom_configs", back_populates="service", cascade="all, delete"
- )
- jobs_cache = relationship(
- "Jobs_cache", back_populates="service", cascade="all, delete"
+ "Custom_configs", back_populates="service", cascade="all"
)
+ jobs_cache = relationship("Jobs_cache", back_populates="service", cascade="all")
class Services_settings(Base):
@@ -139,12 +135,12 @@ class Services_settings(Base):
service_id = Column(
String(64),
- ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
setting_id = Column(
String(256),
- ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
value = Column(String(4096), nullable=False)
@@ -162,7 +158,7 @@ class Jobs(Base):
name = Column(String(128), primary_key=True)
plugin_id = Column(
String(64),
- ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
)
file_name = Column(String(256), nullable=False)
every = Column(SCHEDULES_ENUM, nullable=False)
@@ -171,7 +167,7 @@ class Jobs(Base):
last_run = Column(DateTime, nullable=True)
plugin = relationship("Plugins", back_populates="jobs")
- cache = relationship("Jobs_cache", back_populates="job", cascade="all, delete")
+ cache = relationship("Jobs_cache", back_populates="job", cascade="all")
class Plugin_pages(Base):
@@ -184,7 +180,7 @@ class Plugin_pages(Base):
)
plugin_id = Column(
String(64),
- ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
nullable=False,
)
template_file = Column(LargeBinary(length=(2**32) - 1), nullable=False)
@@ -206,12 +202,12 @@ class Jobs_cache(Base):
)
job_name = Column(
String(128),
- ForeignKey("jobs.name", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("jobs.name", onupdate="cascade", ondelete="cascade"),
nullable=False,
)
service_id = Column(
String(64),
- ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
nullable=True,
)
file_name = Column(
@@ -237,7 +233,7 @@ class Custom_configs(Base):
)
service_id = Column(
String(64),
- ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
nullable=True,
)
type = Column(CUSTOM_CONFIGS_TYPES_ENUM, nullable=False)
@@ -254,7 +250,7 @@ class Selects(Base):
setting_id = Column(
String(256),
- ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
+ ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
value = Column(String(256), primary_key=True)
diff --git a/src/common/gen/Configurator.py b/src/common/gen/Configurator.py
index 331cf470f..b4e035549 100644
--- a/src/common/gen/Configurator.py
+++ b/src/common/gen/Configurator.py
@@ -14,9 +14,11 @@ class Configurator:
self,
settings: str,
core: Union[str, dict],
- plugins: str,
+ plugins: Union[str, dict],
variables: Union[str, dict],
logger: Logger,
+ *,
+ plugins_settings: list = None,
):
self.__logger = logger
self.__settings = self.__load_settings(settings)
@@ -26,8 +28,12 @@ class Configurator:
else:
self.__core = core
- self.__plugins_settings = []
- self.__plugins = self.__load_plugins(plugins, "plugins")
+ self.__plugins_settings = plugins_settings or []
+
+ if isinstance(plugins, str):
+ self.__plugins = self.__load_plugins(plugins, "plugins")
+ else:
+ self.__plugins = plugins
if isinstance(variables, str):
self.__variables = self.__load_variables(variables)
diff --git a/src/common/gen/save_config.py b/src/common/gen/save_config.py
index 2b5fc8947..428a20776 100644
--- a/src/common/gen/save_config.py
+++ b/src/common/gen/save_config.py
@@ -145,6 +145,18 @@ if __name__ == "__main__":
db = None
apis = []
+ plugins = args.plugins
+ plugins_settings = None
+ if not exists("/usr/sbin/nginx") and args.method == "ui":
+ db = Database(logger)
+ plugins = {}
+ plugins_settings = []
+ for plugin in db.get_plugins():
+ del plugin["page"]
+ del plugin["external"]
+ plugins_settings.append(plugin)
+ plugins.update(plugin["settings"])
+
# Check existences and permissions
logger.info("Checking arguments ...")
files = [args.settings] + ([args.variables] if args.variables else [])
@@ -200,7 +212,12 @@ if __name__ == "__main__":
# Compute the config
logger.info("Computing config ...")
config = Configurator(
- args.settings, core_settings, args.plugins, args.variables, logger
+ args.settings,
+ core_settings,
+ plugins,
+ args.variables,
+ logger,
+ plugins_settings=plugins_settings,
)
config_files = config.get_config()
custom_confs = [
@@ -288,7 +305,12 @@ if __name__ == "__main__":
if config_files is None:
logger.info("Computing config ...")
config = Configurator(
- args.settings, core_settings, args.plugins, tmp_config, logger
+ args.settings,
+ core_settings,
+ plugins,
+ tmp_config,
+ logger,
+ plugins_settings=plugins_settings,
)
config_files = config.get_config()
diff --git a/src/ui/main.py b/src/ui/main.py
index caeebd58a..edd7a188f 100755
--- a/src/ui/main.py
+++ b/src/ui/main.py
@@ -1,7 +1,9 @@
from contextlib import suppress
+from importlib.machinery import SourceFileLoader, SourcelessFileLoader
from io import BytesIO
from pathlib import Path
from signal import SIGINT, signal, SIGTERM
+from tempfile import NamedTemporaryFile
from bs4 import BeautifulSoup
from copy import deepcopy
from datetime import datetime, timedelta, timezone
@@ -688,25 +690,40 @@ def plugins():
variables = deepcopy(request.form.to_dict())
del variables["csrf_token"]
- if variables["external"] == "false":
+ if variables["external"] != "True":
flash(f"Can't delete internal plugin {variables['name']}", "error")
return redirect(url_for("loading", next=url_for("plugins"))), 500
- variables["path"] = f"/etc/bunkerweb/plugins/{variables['name']}"
+ if not exists("/usr/sbin/nginx"):
+ plugins = app.config["CONFIG"].get_plugins()
+ for plugin in deepcopy(plugins):
+ if plugin["external"] is False or plugin["id"] == variables["name"]:
+ del plugins[plugins.index(plugin)]
- operation = app.config["CONFIGFILES"].check_path(
- variables["path"], "/etc/bunkerweb/plugins/"
- )
+ err = db.update_external_plugins(plugins)
+ if err:
+ flash(
+ f"Couldn't update external plugins to database: {err}",
+ "error",
+ )
+ else:
+ variables["path"] = f"/etc/bunkerweb/plugins/{variables['name']}"
- if operation:
- flash(operation, "error")
- return redirect(url_for("loading", next=url_for("plugins"))), 500
+ operation = app.config["CONFIGFILES"].check_path(
+ variables["path"], "/etc/bunkerweb/plugins/"
+ )
- operation, error = app.config["CONFIGFILES"].delete_path(variables["path"])
+ if operation:
+ flash(operation, "error")
+ return redirect(url_for("loading", next=url_for("plugins"))), 500
- if error:
- flash(operation, "error")
- return redirect(url_for("loading", next=url_for("plugins")))
+ operation, error = app.config["CONFIGFILES"].delete_path(
+ variables["path"]
+ )
+
+ if error:
+ flash(operation, "error")
+ return redirect(url_for("loading", next=url_for("plugins")))
else:
if not exists("/var/tmp/bunkerweb/ui") or not listdir(
"/var/tmp/bunkerweb/ui"
@@ -758,13 +775,33 @@ def plugins():
)
raise Exception
- if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
- raise FileExistsError
+ if not exists("/usr/sbin/nginx"):
+ plugins = app.config["CONFIG"].get_plugins()
+ for plugin in deepcopy(plugins):
+ if plugin["id"] == folder_name:
+ raise FileExistsError
+ elif plugin["external"] is False:
+ del plugins[plugins.index(plugin)]
- copytree(
- f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
- f"/etc/bunkerweb/plugins/{folder_name}",
- )
+ plugins.append(plugin_file)
+ err = db.update_external_plugins(plugins)
+ if err:
+ error = 1
+ flash(
+ f"Couldn't update external plugins to database: {err}",
+ "error",
+ )
+ raise Exception
+ else:
+ if exists(
+ f"/etc/bunkerweb/plugins/{folder_name}"
+ ):
+ raise FileExistsError
+
+ copytree(
+ f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
+ f"/etc/bunkerweb/plugins/{folder_name}",
+ )
except KeyError:
zip_file.extractall(
f"/var/tmp/bunkerweb/ui/{temp_folder_name}"
@@ -812,13 +849,33 @@ def plugins():
)
raise Exception
- if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
- raise FileExistsError
+ if not exists("/usr/sbin/nginx"):
+ plugins = app.config["CONFIG"].get_plugins()
+ for plugin in deepcopy(plugins):
+ if plugin["id"] == folder_name:
+ raise FileExistsError
+ elif plugin["external"] is False:
+ del plugins[plugins.index(plugin)]
- copytree(
- f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
- f"/etc/bunkerweb/plugins/{folder_name}",
- )
+ plugins.append(plugin_file)
+ err = db.update_external_plugins(plugins)
+ if err:
+ error = 1
+ flash(
+ f"Couldn't update external plugins to database: {err}",
+ "error",
+ )
+ raise Exception
+ else:
+ if exists(
+ f"/etc/bunkerweb/plugins/{folder_name}"
+ ):
+ raise FileExistsError
+
+ copytree(
+ f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
+ f"/etc/bunkerweb/plugins/{folder_name}",
+ )
except BadZipFile:
errors += 1
error = 1
@@ -861,13 +918,33 @@ def plugins():
)
raise Exception
- if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
- raise FileExistsError
+ if not exists("/usr/sbin/nginx"):
+ plugins = app.config["CONFIG"].get_plugins()
+ for plugin in deepcopy(plugins):
+ if plugin["id"] == folder_name:
+ raise FileExistsError
+ elif plugin["external"] is False:
+ del plugins[plugins.index(plugin)]
- copytree(
- f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
- f"/etc/bunkerweb/plugins/{folder_name}",
- )
+ plugins.append(plugin_file)
+ err = db.update_external_plugins(plugins)
+ if err:
+ error = 1
+ flash(
+ f"Couldn't update external plugins to database: {err}",
+ "error",
+ )
+ raise Exception
+ else:
+ if exists(
+ f"/etc/bunkerweb/plugins/{folder_name}"
+ ):
+ raise FileExistsError
+
+ copytree(
+ f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
+ f"/etc/bunkerweb/plugins/{folder_name}",
+ )
except KeyError:
tar_file.extractall(
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
@@ -915,13 +992,33 @@ def plugins():
)
raise Exception
- if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
- raise FileExistsError
+ if not exists("/usr/sbin/nginx"):
+ plugins = app.config["CONFIG"].get_plugins()
+ for plugin in deepcopy(plugins):
+ if plugin["id"] == folder_name:
+ raise FileExistsError
+ elif plugin["external"] is False:
+ del plugins[plugins.index(plugin)]
- copytree(
- f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
- f"/etc/bunkerweb/plugins/{folder_name}",
- )
+ plugins.append(plugin_file)
+ err = db.update_external_plugins(plugins)
+ if err:
+ error = 1
+ flash(
+ f"Couldn't update external plugins to database: {err}",
+ "error",
+ )
+ raise Exception
+ else:
+ if exists(
+ f"/etc/bunkerweb/plugins/{folder_name}"
+ ):
+ raise FileExistsError
+
+ copytree(
+ f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
+ f"/etc/bunkerweb/plugins/{folder_name}",
+ )
except ReadError:
errors += 1
error = 1
@@ -993,14 +1090,12 @@ def plugins():
# Fix permissions for plugins folders
for root, dirs, files in walk("/etc/bunkerweb/plugins", topdown=False):
for name in files + dirs:
- chown(join(root, name), 101, 101)
+ chown(join(root, name), "root", 101)
chmod(join(root, name), 0o770)
if operation:
flash(operation)
- app.config["CONFIG"].reload_plugins()
-
# Reload instances
app.config["RELOADING"] = True
Thread(
@@ -1023,19 +1118,28 @@ def plugins():
if request.args.get("plugin_id", False):
plugin_id = request.args.get("plugin_id")
- page_path = ""
+ template = None
- if exists(f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"):
- page_path = f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"
- elif exists(f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"):
- page_path = f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"
+ if not exists("/usr/sbin/nginx"):
+ page = db.get_plugin_template(plugin_id)
+
+ if page is not None:
+ template = Template(page.decode("utf-8"))
else:
- flash(f"Plugin {plugin_id} not found", "error")
+ page_path = ""
- if page_path:
- with open(page_path, "r") as f:
- template = Template(f.read())
+ if exists(f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"):
+ page_path = f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"
+ elif exists(f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"):
+ page_path = f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"
+ else:
+ flash(f"Plugin {plugin_id} not found", "error")
+ if page_path:
+ with open(page_path, "r") as f:
+ template = Template(f.read())
+
+ if template is not None:
return template.render(
csrf_token=generate_csrf,
url_for=url_for,
@@ -1047,7 +1151,6 @@ def plugins():
),
)
- app.config["CONFIG"].reload_plugins()
plugins = app.config["CONFIG"].get_plugins()
plugins_internal = 0
plugins_external = 0
@@ -1096,33 +1199,66 @@ def custom_plugin(plugin):
)
return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
- if not exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py") and not exists(
- f"/usr/share/bunkerweb/core/{plugin}/ui/actions.py"
- ):
- flash(
- f"The actions.py file for the plugin {plugin} does not exist",
- "error",
- )
- return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
+ if not exists("/usr/sbin/nginx"):
+ module = db.get_plugin_actions(plugin)
- # Add the custom plugin to sys.path
- sys_path.append(
- (
- "/etc/bunkerweb/plugins"
- if exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py")
- else "/usr/share/bunkerweb/core"
+ if module is None:
+ flash(
+ f"The actions.py file for the plugin {plugin} does not exist",
+ "error",
+ )
+ return redirect(
+ url_for("loading", next=url_for("plugins", plugin_id=plugin))
+ )
+
+ try:
+ # Try to import the custom plugin
+ with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
+ temp.write(module)
+ temp.flush()
+ temp.seek(0)
+ loader = SourceFileLoader("actions", temp.name)
+ actions = loader.load_module()
+ except:
+ flash(
+ f"An error occurred while importing the plugin {plugin}:
{format_exc()}",
+ "error",
+ )
+ return redirect(
+ url_for("loading", next=url_for("plugins", plugin_id=plugin))
+ )
+ else:
+ if not exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py") and not exists(
+ f"/usr/share/bunkerweb/core/{plugin}/ui/actions.py"
+ ):
+ flash(
+ f"The actions.py file for the plugin {plugin} does not exist",
+ "error",
+ )
+ return redirect(
+ url_for("loading", next=url_for("plugins", plugin_id=plugin))
+ )
+
+ # Add the custom plugin to sys.path
+ sys_path.append(
+ (
+ "/etc/bunkerweb/plugins"
+ if exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py")
+ else "/usr/share/bunkerweb/core"
+ )
+ + f"/{plugin}/ui/"
)
- + f"/{plugin}/ui/"
- )
- try:
- # Try to import the custom plugin
- import actions
- except:
- flash(
- f"An error occurred while importing the plugin {plugin}:
{format_exc()}",
- "error",
- )
- return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
+ try:
+ # Try to import the custom plugin
+ import actions
+ except:
+ flash(
+ f"An error occurred while importing the plugin {plugin}:
{format_exc()}",
+ "error",
+ )
+ return redirect(
+ url_for("loading", next=url_for("plugins", plugin_id=plugin))
+ )
error = False
res = None
@@ -1145,10 +1281,11 @@ def custom_plugin(plugin):
)
error = True
finally:
- # Remove the custom plugin from the shared library
- sys_path.pop()
- sys_modules.pop("actions")
- del actions
+ if exists("/usr/sbin/nginx"):
+ # Remove the custom plugin from the shared library
+ sys_path.pop()
+ sys_modules.pop("actions")
+ del actions
if (
request.method != "POST"
diff --git a/src/ui/src/Config.py b/src/ui/src/Config.py
index df4f32c96..563ca2b78 100644
--- a/src/ui/src/Config.py
+++ b/src/ui/src/Config.py
@@ -1,6 +1,7 @@
from copy import deepcopy
-from os import listdir, remove
+from os import listdir, mkdir, remove
from pathlib import Path
+from shutil import rmtree
from time import sleep
from flask import flash
from os.path import exists, isfile
@@ -35,54 +36,6 @@ class Config:
sleep(3)
env = self.__db.get_config()
- self.reload_plugins()
-
- def reload_plugins(self) -> None:
- self.__plugins = []
- external_plugins = []
-
- for foldername in list(iglob("/etc/bunkerweb/plugins/*")) + list(
- iglob("/usr/share/bunkerweb/core/*")
- ):
- content = listdir(foldername)
- if "plugin.json" not in content:
- continue
-
- with open(f"{foldername}/plugin.json", "r") as f:
- plugin = json_load(f)
-
- plugin.update(
- {
- "page": False,
- "external": foldername.startswith("/etc/bunkerweb/plugins"),
- }
- )
-
- if plugin["external"] is True:
- external_plugin = deepcopy(plugin)
- del external_plugin["external"]
- del external_plugin["page"]
- external_plugins.append(external_plugin)
-
- if "ui" in content:
- if "template.html" in listdir(f"{foldername}/ui"):
- plugin["page"] = True
-
- self.__plugins.append(plugin)
-
- self.__plugins.sort(key=lambda plugin: plugin.get("name"))
- self.__plugins_settings = {
- **{k: v for x in self.__plugins for k, v in x["settings"].items()},
- **self.__settings,
- }
-
- if external_plugins:
- err = self.__db.update_external_plugins(external_plugins)
- if err:
- self.__logger.error(
- f"Couldn't update external plugins to database: {err}",
- )
-
def __env_to_dict(self, filename: str) -> dict:
"""Converts the content of an env file into a dict
@@ -139,17 +92,17 @@ class Config:
conf = deepcopy(global_conf)
servers = []
+ plugins_settings = self.get_plugins_settings()
for service in services_conf:
server_name = service["SERVER_NAME"].split(" ")[0]
for k in service.keys():
key_without_server_name = k.replace(f"{server_name}_", "")
if (
- self.__plugins_settings[key_without_server_name]["context"]
- != "global"
- if key_without_server_name in self.__plugins_settings
+ plugins_settings[key_without_server_name]["context"] != "global"
+ if key_without_server_name in plugins_settings
else True
):
- if not k.startswith(server_name) or k in self.__plugins_settings:
+ if not k.startswith(server_name) or k in plugins_settings:
conf[f"{server_name}_{k}"] = service[k]
else:
conf[k] = service[k]
@@ -178,10 +131,44 @@ class Config:
remove(env_file)
def get_plugins_settings(self) -> dict:
- return self.__plugins_settings
+ return {
+ **{k: v for x in self.get_plugins() for k, v in x["settings"].items()},
+ **self.__settings,
+ }
def get_plugins(self) -> List[dict]:
- return self.__plugins
+ if not exists("/usr/sbin/nginx"):
+ plugins = self.__db.get_plugins()
+ plugins.sort(key=lambda x: x["name"])
+ return plugins
+
+ plugins = []
+
+ for foldername in list(iglob("/etc/bunkerweb/plugins/*")) + list(
+ iglob("/usr/share/bunkerweb/core/*")
+ ):
+ content = listdir(foldername)
+ if "plugin.json" not in content:
+ continue
+
+ with open(f"{foldername}/plugin.json", "r") as f:
+ plugin = json_load(f)
+
+ plugin.update(
+ {
+ "page": False,
+ "external": foldername.startswith("/etc/bunkerweb/plugins"),
+ }
+ )
+
+ if "ui" in content:
+ if "template.html" in listdir(f"{foldername}/ui"):
+ plugin["page"] = True
+
+ plugins.append(plugin)
+
+ plugins.sort(key=lambda x: x["name"])
+ return plugins
def get_settings(self) -> dict:
return self.__settings
@@ -212,6 +199,7 @@ class Config:
"""
if exists("/usr/sbin/nginx"):
services = []
+ plugins_settings = self.get_plugins_settings()
for filename in iglob("/etc/nginx/**/variables.env"):
service = filename.split("/")[3]
env = {
@@ -219,8 +207,7 @@ class Config:
{"value": v, "method": "ui"} if methods is True else v
)
for k, v in self.__env_to_dict(filename).items()
- if k.startswith(f"{service}_")
- or k in self.__plugins_settings.keys()
+ if k.startswith(f"{service}_") or k in plugins_settings.keys()
}
services.append(env)
@@ -242,11 +229,12 @@ class Config:
Return the error code
"""
error = 0
+ plugins_settings = self.get_plugins_settings()
for k, v in variables.items():
check = False
- if k in self.__plugins_settings:
- if _global ^ (self.__plugins_settings[k]["context"] == "global"):
+ if k in plugins_settings:
+ if _global ^ (plugins_settings[k]["context"] == "global"):
error = 1
flash(f"Variable {k} is not valid.", "error")
continue
@@ -255,16 +243,16 @@ class Config:
else:
setting = k[0 : k.rfind("_")]
if (
- setting not in self.__plugins_settings
- or "multiple" not in self.__plugins_settings[setting]
+ setting not in plugins_settings
+ or "multiple" not in plugins_settings[setting]
):
error = 1
flash(f"Variable {k} is not valid.", "error")
continue
if not (
- _global ^ (self.__plugins_settings[setting]["context"] == "global")
- ) and re_search(self.__plugins_settings[setting]["regex"], v):
+ _global ^ (plugins_settings[setting]["context"] == "global")
+ ) and re_search(plugins_settings[setting]["regex"], v):
check = True
if not check: