From 11eedbb191fcbad63a744bbc81772d42a4f1af64 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Sun, 22 Sep 2024 22:23:13 +0200 Subject: [PATCH 1/2] Include mod_jsonrpc --- .gitignore | 1 - README.md | 6 + core/exemple_configuration.json | 6 + core/installation.py | 2 +- core/loadConf.py | 15 ++ mods/mod_jsonrpc.py | 248 ++++++++++++++++++++++++++++++++ 6 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 mods/mod_jsonrpc.py diff --git a/.gitignore b/.gitignore index 2d90c1a..9867c4e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ db/ logs/ __pycache__/ -mods/mod_jsonrpc.py configuration.json *.log test.py \ No newline at end of file diff --git a/README.md b/README.md index 1166ff9..72fd0cf 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,12 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant "OWNER": "TON_NICK_NAME", "PASSWORD": "TON_PASSWORD", + "JSONRPC_URL": "https://your.domaine.com:8600/api", + "JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket", + "JSONRPC_METHOD": "socket", + "JSONRPC_USER": "YOUR_RPC_USER", + "JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD", + "SALON_JAIL": "#jail", "SALON_JAIL_MODES": "sS", "SALON_LIBERER": "#welcome", diff --git a/core/exemple_configuration.json b/core/exemple_configuration.json index 05b77c6..dbc5145 100644 --- a/core/exemple_configuration.json +++ b/core/exemple_configuration.json @@ -22,6 +22,12 @@ "OWNER": "TON_NICK_NAME", "PASSWORD": "TON_PASSWORD", + "JSONRPC_URL": "https://your.domaine.com:8600/api", + "JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket", + "JSONRPC_METHOD": "socket", + "JSONRPC_USER": "YOUR_RPC_USER", + "JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD", + "SALON_JAIL": "#jail", "SALON_JAIL_MODES": "sS", "SALON_LIBERER": "#welcome", diff --git a/core/installation.py b/core/installation.py index c424e01..4d14d80 100644 --- a/core/installation.py +++ b/core/installation.py @@ -64,7 +64,7 @@ class Install: defender_install_folder=defender_install_folder, venv_folder=venv_folder, venv_cmd_installation=['python3', '-m', 'venv', venv_folder], - venv_cmd_requirements=['sqlalchemy','psutil','requests','faker'], + venv_cmd_requirements=['sqlalchemy','psutil','requests','faker','unrealircd_rpc_py'], venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip', venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' ) diff --git a/core/loadConf.py b/core/loadConf.py index 4318542..675f08f 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -84,6 +84,12 @@ class ConfigDataModel: PASSWORD: str """The password of the admin of the service""" + JSONRPC_URL: str + JSONRPC_PATH_TO_SOCKET_FILE: str + JSONRPC_METHOD: str + JSONRPC_USER: str + JSONRPC_PASSWORD: str + SALON_JAIL: str """The JAIL channel (ex. #jail)""" @@ -208,13 +214,22 @@ class Config: SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!', OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin', PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin', + + JSONRPC_METHOD=import_config["JSONRPC_METHOD"] if "JSONRPC_METHOD" in import_config else 'socket', + JSONRPC_URL=import_config["JSONRPC_URL"] if "JSONRPC_URL" in import_config else None, + JSONRPC_PATH_TO_SOCKET_FILE=import_config["JSONRPC_PATH_TO_SOCKET_FILE"] if "JSONRPC_PATH_TO_SOCKET_FILE" in import_config else None, + JSONRPC_USER=import_config["JSONRPC_USER"] if "JSONRPC_USER" in import_config else None, + JSONRPC_PASSWORD=import_config["JSONRPC_PASSWORD"] if "JSONRPC_PASSWORD" in import_config else None, + SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail', SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS', SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome', + CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones', CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts', CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [], CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234", + API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2, PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [], WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'], diff --git a/mods/mod_jsonrpc.py b/mods/mod_jsonrpc.py new file mode 100644 index 0000000..8bacb00 --- /dev/null +++ b/mods/mod_jsonrpc.py @@ -0,0 +1,248 @@ +from dataclasses import dataclass +from core.irc import Irc +from unrealircd_rpc_py.Live import Live +from unrealircd_rpc_py.Loader import Loader + + +class Jsonrpc(): + + @dataclass + class ModConfModel: + """The Model containing the module parameters + """ + param_exemple1: str + param_exemple2: int + + def __init__(self, ircInstance:Irc) -> None: + + # Module name (Mandatory) + self.module_name = 'mod_' + str(self.__class__.__name__).lower() + + # Add Irc Object to the module (Mandatory) + self.Irc = ircInstance + + # Add Global Configuration to the module (Mandatory) + self.Config = ircInstance.Config + + # Add Base object to the module (Mandatory) + self.Base = ircInstance.Base + + # Add logs object to the module (Mandatory) + self.Logs = ircInstance.Base.logs + + # Add User object to the module (Mandatory) + self.User = ircInstance.User + + # Add Channel object to the module (Mandatory) + self.Channel = ircInstance.Channel + + # Create module commands (Mandatory) + self.commands_level = { + 1: ['jsonrpc', 'jruser'] + } + + # Init the module + self.__init_module() + + # Log the module + self.Logs.debug(f'Module {self.module_name} loaded ...') + + def __init_module(self) -> None: + + # Insert module commands into the core one (Mandatory) + self.__set_commands(self.commands_level) + + # Create you own tables (Mandatory) + # self.__create_tables() + + # Load module configuration and sync with core one (Mandatory) + self.__load_module_configuration() + # End of mandatory methods you can start your customization # + + self.UnrealIrcdRpcLive: Live = None + self.Rpc: Loader = Loader( + req_method=self.Config.JSONRPC_METHOD, + url=self.Config.JSONRPC_URL, + username=self.Config.JSONRPC_USER, + password=self.Config.JSONRPC_PASSWORD + ) + if self.Rpc.Error.code != -1: + self.Base.db_delete_module(module_name=self.module_name) + + return None + + def __set_commands(self, commands:dict[int, list[str]]) -> None: + """### Rajoute les commandes du module au programme principal + + Args: + commands (list): Liste des commandes du module + """ + for level, com in commands.items(): + for c in commands[level]: + if not c in self.Irc.commands: + self.Irc.commands_level[level].append(c) + self.Irc.commands.append(c) + + return None + + def __create_tables(self) -> None: + """Methode qui va créer la base de donnée si elle n'existe pas. + Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module + Args: + database_name (str): Nom de la base de données ( pas d'espace dans le nom ) + + Returns: + None: Aucun retour n'es attendu + """ + + table_logs = '''CREATE TABLE IF NOT EXISTS test_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + datetime TEXT, + server_msg TEXT + ) + ''' + + self.Base.db_execute_query(table_logs) + return None + + def callback_sent_to_irc(self, json_response: str): + + dnickname = self.Config.SERVICE_NICKNAME + dchanlog = self.Config.SERVICE_CHANLOG + + self.Irc.sendPrivMsg(msg=json_response, channel=dchanlog) + + def thread_start_jsonrpc(self): + + self.UnrealIrcdRpcLive = Live(path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE, + callback_object_instance=self, + callback_method_name='callback_sent_to_irc' + ) + self.UnrealIrcdRpcLive.subscribe() + + def __load_module_configuration(self) -> None: + """### Load Module Configuration + """ + try: + # Build the default configuration model (Mandatory) + self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1) + + # Sync the configuration with core configuration (Mandatory) + #self.Base.db_sync_core_config(self.module_name, self.ModConfig) + + return None + + except TypeError as te: + self.Logs.critical(te) + + def __update_configuration(self, param_key: str, param_value: str): + """Update the local and core configuration + + Args: + param_key (str): The parameter key + param_value (str): The parameter value + """ + self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) + + def unload(self) -> None: + + return None + + def cmd(self, data:list) -> None: + + return None + + def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: + + command = str(cmd[0]).lower() + dnickname = self.Config.SERVICE_NICKNAME + fromuser = user + fromchannel = str(channel) if not channel is None else None + + match command: + + case 'jsonrpc': + try: + option = str(cmd[1]).lower() + + if len(command) == 1: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc on') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc off') + + match option: + + case 'on': + self.Base.create_thread(self.thread_start_jsonrpc, run_once=True) + + case 'off': + self.UnrealIrcdRpcLive.unsubscribe() + + except IndexError as ie: + self.Logs.error(ie) + + case 'jruser': + try: + option = str(cmd[1]).lower() + + if len(command) == 1: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jruser get nickname') + + match option: + + case 'get': + nickname = str(cmd[2]) + uid_to_get = self.User.get_uid(nickname) + if uid_to_get is None: + return None + + rpc = Loader( + req_method=self.Config.JSONRPC_METHOD, + url=self.Config.JSONRPC_URL, + username=self.Config.JSONRPC_USER, + password=self.Config.JSONRPC_PASSWORD + ) + + UserInfo = rpc.User.get(uid_to_get) + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.username}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.realname}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {UserInfo.channels}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.security_groups}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.reputation}') + + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IP : {UserInfo.ip}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.country_code}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.asn}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.asname}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.cloakedhost}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :HOSTNAME : {UserInfo.hostname}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.vhost}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLIENT PORT : {UserInfo.client_port}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SERVER PORT : {UserInfo.server_port}') + + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IDLE SINCE : {UserInfo.idle_since}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CONNECTED SINCE : {UserInfo.connected_since}') + + except IndexError as ie: + self.Logs.error(ie) + + case 'ia': + try: + + self.Base.create_thread(self.thread_ask_ia, ('',)) + + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...") + self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...") + + if not fromchannel is None: + self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...") + + # How to update your module configuration + self.__update_configuration('param_exemple2', 7) + + # Log if you want the result + self.Logs.debug(f"Test logs ready") + + except Exception as err: + self.Logs.error(f"Unknown Error: {err}") \ No newline at end of file From ee02566343b60587b87bf2f745095f1a1709bb18 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:20:12 +0200 Subject: [PATCH 2/2] V5.3.1 --- mods/mod_jsonrpc.py | 13 ++++++++++--- version.json | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mods/mod_jsonrpc.py b/mods/mod_jsonrpc.py index 8bacb00..743eeac 100644 --- a/mods/mod_jsonrpc.py +++ b/mods/mod_jsonrpc.py @@ -66,8 +66,8 @@ class Jsonrpc(): username=self.Config.JSONRPC_USER, password=self.Config.JSONRPC_PASSWORD ) - if self.Rpc.Error.code != -1: - self.Base.db_delete_module(module_name=self.module_name) + if self.Rpc.Error.code != 0: + self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG) return None @@ -118,7 +118,10 @@ class Jsonrpc(): callback_object_instance=self, callback_method_name='callback_sent_to_irc' ) - self.UnrealIrcdRpcLive.subscribe() + if self.UnrealIrcdRpcLive.Error.code == 0: + self.UnrealIrcdRpcLive.subscribe() + else: + self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG) def __load_module_configuration(self) -> None: """### Load Module Configuration @@ -203,6 +206,10 @@ class Jsonrpc(): ) UserInfo = rpc.User.get(uid_to_get) + if rpc.Error.code != 0: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}') + return None + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.username}') diff --git a/version.json b/version.json index 21edfcd..85ed6d2 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.3.0" + "version": "5.3.1" } \ No newline at end of file