From 683f0444c2b72fbdaf018bf51b083da6ff47e085 Mon Sep 17 00:00:00 2001 From: Chen1108 Date: Tue, 6 Aug 2024 17:32:34 +0800 Subject: [PATCH] Add files via upload --- main.py | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ safeQ.ico | Bin 0 -> 31289 bytes 2 files changed, 215 insertions(+) create mode 100644 main.py create mode 100644 safeQ.ico diff --git a/main.py b/main.py new file mode 100644 index 0000000..4aa6486 --- /dev/null +++ b/main.py @@ -0,0 +1,215 @@ +# pyinstaller -F -c -i .\safeQ.ico -n safeq_exporter .\main.py +import requests +import uuid +from bs4 import BeautifulSoup +import logging +import datetime +import os +import sys +import argparse +import configparser +import time + +# Configuration file path +DEFAULT_CONFIG_FILE = os.path.dirname(os.path.abspath(__file__)) + "/config.ini" + + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) + + +def load_config(config_file): + """Load configuration from file.""" + if not os.path.exists(config_file): + logging.error(f"Config file {config_file} not found.") + create_config_file(config_file) + sys.exit(1) + + config = configparser.ConfigParser() + config.read(config_file) + + # Check for missing parameters + required_params = ["BASE_URL", "USERNAME", "PASSWORD", "FILEPATH"] + for section in config.sections(): + missing_params = [ + param for param in required_params if param not in config[section] + ] + if missing_params: + logging.error( + f"Missing parameters in section {section}: {', '.join(missing_params)}" + ) + input( + f"\n!!! Please edit the config file [ {config_file} ] to include all required parameters.\n" + ) + sys.exit(1) + + return config + + +def create_config_file(config_file): + """Create a template configuration file.""" + config = configparser.ConfigParser() + + for i in range(1, 4): + section = f"Server{i}" + config[section] = { + "BASE_URL": f"http://server{i}.example.com", + "USERNAME": f"admin{i}", + "PASSWORD": f"password{i}", + "FILEPATH": f"./downloads{i}", + } + + try: + with open(config_file, "w") as f: + config.write(f) + input( + f"!!! Config file [ {config_file} ] has been created. Please edit it with your actual settings." + ) + except IOError as e: + logging.error(f"Failed to create config file. Error: {str(e)}") + + +class UserExporter: + def __init__(self, base_url): + self.base_url = base_url + self.session = requests.Session() + self.csrf_token = None + self.timeout = 10 # Set a default timeout of 10 seconds + + def login(self, username, password): + login_url = f"{self.base_url}/login" + self.csrf_token = str(uuid.uuid4()) + + login_data = { + "username": username, + "password": password, + "_csrf": self.csrf_token, + } + + try: + response = self.session.post( + login_url, data=login_data, timeout=self.timeout + ) + response.raise_for_status() # Raise an exception for bad status codes + + logging.info("Login successful") + jsessionid = self.session.cookies.get("JSESSIONID") + logging.info(f"JSESSIONID: {jsessionid}") + self.update_csrf_token() + return True + except requests.Timeout: + logging.error(f"Login request timed out after {self.timeout} seconds") + except requests.ConnectionError: + logging.error( + "Failed to connect to the server. Please check your internet connection and the server status." + ) + except requests.RequestException as e: + logging.error(f"Login failed. Error: {str(e)}") + return False + + def update_csrf_token(self): + try: + response = self.session.get( + f"{self.base_url}/web/Dashboard", timeout=self.timeout + ) + response.raise_for_status() + soup = BeautifulSoup(response.content, "html.parser") + self.csrf_token = soup.find("meta", {"name": "_csrf"})["content"] + logging.info(f"Updated CSRF Token: {self.csrf_token}") + except requests.Timeout: + logging.error( + f"CSRF token update request timed out after {self.timeout} seconds" + ) + except requests.RequestException as e: + logging.error(f"Failed to update CSRF token. Error: {str(e)}") + + def export_users(self, filepath, section): + export_url = f"{self.base_url}/servlet/users.ExportUsersServlet" + + export_data = { + "redirect": "/WEB-INF/views/legacy/web/UserList.jsp", + "charset": "UTF8", + "users": "1", + "_csrf": self.csrf_token, + } + + try: + response = self.session.post( + export_url, data=export_data, timeout=self.timeout + ) + response.raise_for_status() + + filename = f"{section}_exported_users_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + full_path = os.path.join(filepath, filename) + self.save_file(full_path, response.content) + logging.info(f"File '{filename}' has been saved to {full_path}.") + return True + except requests.Timeout: + logging.error(f"Export request timed out after {self.timeout} seconds") + except requests.RequestException as e: + logging.error(f"Failed to export users. Error: {str(e)}") + return False + + @staticmethod + def save_file(filename, content): + try: + os.makedirs(os.path.dirname(filename), exist_ok=True) + with open(filename, "wb") as file: + file.write(content) + except IOError as e: + logging.error(f"Failed to save file. Error: {str(e)}") + + +def main(): + print( + """ + + ░▒▓███████▓▒░░▒▓██████▓▒░░▒▓████████▓▒░▒▓████████▓▒░▒▓██████▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ +░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ + ░▒▓██████▓▒░░▒▓████████▓▒░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░ + ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ + ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ +░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓████████▓▒░▒▓██████▓▒░ + ░▒▓█▓▒░ +SafeQ Export Tool Ver.2024.08.06 ░▒▓█▓▒░ +----------------------------------------------------------------- + +Usage: safeq_exporter.exe [-h] [-c CONFIG] + +""" + ) + parser = argparse.ArgumentParser(description="Export users from multiple servers.") + parser.add_argument( + "-c", + "--config", + default=DEFAULT_CONFIG_FILE, + help="Path to the configuration file", + ) + args = parser.parse_args() + + config = load_config(args.config) + + for section in config.sections(): + logging.info(f"Processing server: {section}") + base_url = config[section]["BASE_URL"] + username = config[section]["USERNAME"] + password = config[section]["PASSWORD"] + filepath = config[section]["FILEPATH"] + + exporter = UserExporter(base_url) + if exporter.login(username, password): + exporter.export_users(filepath, section) + else: + logging.error(f"Failed to process server {section}") + + # 倒计时 退出程序 + for i in range(10, 0, -1): + print("\rThis App will terminate in {} seconds.".format(i), end="", flush=True) + time.sleep(1) + print("\rBye-bye! ") + + +if __name__ == "__main__": + main() diff --git a/safeQ.ico b/safeQ.ico new file mode 100644 index 0000000000000000000000000000000000000000..0bd541ea6df4f2884607d0f06d4e5847bf40c8a2 GIT binary patch literal 31289 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 UOx=R!M^_`o*uTO2!9aiiKOf6+CjbBd literal 0 HcmV?d00001