diff --git a/README.md b/README.md index 972cbde..570330f 100644 --- a/README.md +++ b/README.md @@ -207,18 +207,12 @@ class LogSeverity(Enum): ERROR = "ERROR" -class ActionType(Enum): - GENERATE_CERT = "GENERATE_CERT" - REVOKE_CERT = "REVOKE_CERT" - # etc - - @dataclass class CommandInfo: command: str output: str exit_code: int - action: ActionType + action: str @dataclass @@ -358,7 +352,7 @@ paths: $ref: '#/components/schemas/LogEntry' - /logs: #TODO simplify? + /logs: get: summary: Retrieve logs parameters: @@ -507,6 +501,186 @@ components: exitCode: type: integer ``` + +### Class Diagram +```puml +@startuml + +package "Shared Components" { + class CLIWrapper { + - sanitize_input(input) + + execute_command(command) + } + + class Logger { + - log_file: str + + log(severity, message, trace_id, command_info) + + get_logs(filters) + + get_log_entry(log_id) + } + + enum LogSeverity { + DEBUG + INFO + WARN + ERROR + } + + class CommandInfo { + + command: str + + output: str + + exit_code: int + + action: str + } + + class LogEntry { + + entry_id: int + + timestamp: datetime + + severity: LogSeverity + + message: str + + trace_id: UUID + + command_info: CommandInfo + } + + Logger --> LogSeverity : uses + Logger --> LogEntry : creates + LogEntry --> CommandInfo : contains +} + +package "WebFrontend Container" { + class FlaskWebServer { + - app: Flask + + run() + + route_dashboard() + + route_logs() + + route_certificate_management() + } + + class UIComponents { + + render_dashboard() + + render_logs_page() + + render_certificate_list() + + render_generate_cert_modal() + } + + class APIClient { + - base_url: str + - api_key: str + + get_certificates() + + generate_certificate(params) + + renew_certificate(cert_id, duration) + + revoke_certificate(cert_id) + + get_logs(filters) + } + + FlaskWebServer --> UIComponents : uses + FlaskWebServer --> APIClient : uses +} + +package "Core Container" { + class MainApplication { + + initialize() + + run() + } + + class APIServer { + - app: Flask + + run() + + route_certificates() + + route_generate_certificate() + + route_renew_certificate() + + route_revoke_certificate() + + route_logs() + } + + class CertificateManager { + + list_certificates() + + generate_certificate(params) + + renew_certificate(cert_id, duration) + + revoke_certificate(cert_id) + } + + MainApplication --> APIServer : initializes + MainApplication --> CertificateManager : uses + MainApplication --> Logger : uses + + APIServer --> CertificateManager : uses + APIServer --> Logger : uses + + CertificateManager --> CLIWrapper : uses + CertificateManager --> Logger : uses +} + +package "AutoSetup Component" { + class AutoSetup { + + install_stepca() + + setup_stepca() + + create_root_certificate() + + prepare_mtls_certificates() + + run_docker_compose() + } + + AutoSetup --> CLIWrapper : uses + AutoSetup --> Logger : uses +} + + +package "Host System" { + rectangle "step-ca CLI" as stepca { + } +} + +CLIWrapper --> stepca : executes commands + +APIClient ..> APIServer : communicates via mTLS + +@enduml +``` + +### File structure +``` +project_root/ +│ +├── docker-compose.yml +├── README.md +│ +├── shared/ +│ ├── __init__.py +│ ├── cli_wrapper.py +│ ├── logger.py +│ └── models.py +│ +├── web_frontend/ +│ ├── Dockerfile +│ ├── requirements.txt +│ ├── app.py +│ ├── api_client.py +│ ├── ui_components.py +│ └── templates/ +│ ├── base.html +│ ├── dashboard.html +│ ├── logs.html +│ ├── certificate_management.html +│ └── modals/ +│ └── generate_cert.html +│ +├── core/ +│ ├── Dockerfile +│ ├── requirements.txt +│ ├── main.py +│ ├── api_server.py +│ └── certificate_manager.py +│ +├── auto_setup/ +│ ├── auto_setup.py +│ └── requirements.txt +│ +└── config/ + ├── app_config.yml + └── logging_config.yml +``` + + --- # TODO \ No newline at end of file diff --git a/app/auth.py b/app/auth.py deleted file mode 100644 index 2d07e96..0000000 --- a/app/auth.py +++ /dev/null @@ -1,23 +0,0 @@ -from functools import wraps -from flask import request, jsonify -from werkzeug.security import check_password_hash - - -class Auth: - def __init__(self, config): - self.config = config - - def login_required(self, f): - @wraps(f) - def decorated_function(*args, **kwargs): - auth = request.authorization - if not auth or not self.verify_credentials(auth.username, auth.password): - return jsonify({"message": "Authentication required"}), 401 - return f(*args, **kwargs) - - return decorated_function - - def verify_credentials(self, username, password): - stored_username = self.config.get_setting('username') - stored_password_hash = self.config.get_setting('password_hash') - return username == stored_username and check_password_hash(stored_password_hash, password) diff --git a/app/certificate_manager.py b/app/certificate_manager.py deleted file mode 100644 index 7538b91..0000000 --- a/app/certificate_manager.py +++ /dev/null @@ -1,24 +0,0 @@ -from step_ca_shared.cli_wrapper import CLIWrapper - - -class CertManager: - def __init__(self): - self.cli = CLIWrapper() - - def list_certificates(self): - result = self.cli.run_step_ca_command(["certificate", "list"]) - # Parse the output and return a list of certificates - return result - - def generate_certificate(self, params): - command = ["certificate", "create", params["name"], params["domain"]] - result = self.cli.run_step_ca_command(command) - return result - - def revoke_certificate(self, cert_id): - result = self.cli.run_step_ca_command(["certificate", "revoke", cert_id]) - return result - - def renew_certificate(self, cert_id): - result = self.cli.run_step_ca_command(["certificate", "renew", cert_id]) - return result diff --git a/app/config.py b/app/config.py deleted file mode 100644 index 87d5c8f..0000000 --- a/app/config.py +++ /dev/null @@ -1,19 +0,0 @@ -import yaml - - -class Config: - def __init__(self, config_path="config.yaml"): - self.config_path = config_path - self.config = self.load_config() - - def load_config(self): - with open(self.config_path, 'r') as file: - return yaml.safe_load(file) - - def get_setting(self, key): - return self.config.get(key) - - def update_setting(self, key, value): - self.config[key] = value - with open(self.config_path, 'w') as file: - yaml.dump(self.config, file) diff --git a/app/main.py b/app/main.py deleted file mode 100644 index 11637aa..0000000 --- a/app/main.py +++ /dev/null @@ -1,71 +0,0 @@ -from flask import Flask, jsonify, request -from .config import Config -from .auth import Auth -from .certificate_manager import CertManager -from .server_config import ServerConfig -from .log_handler import LogHandler - -app = Flask(__name__) -config = Config() -auth = Auth(config) -cert_manager = CertManager() -server_config = ServerConfig() -log_handler = LogHandler() - - -@app.route('/') -@auth.login_required -def index(): - return jsonify({"message": "Welcome to step-ca web UI"}) - - -@app.route('/certificates', methods=['GET', 'POST']) -@auth.login_required -def certificates(): - if request.method == 'GET': - return jsonify(cert_manager.list_certificates()) - elif request.method == 'POST': - params = request.json - return jsonify(cert_manager.generate_certificate(params)) - - -@app.route('/certificates/', methods=['DELETE', 'PUT']) -@auth.login_required -def certificate_operations(cert_id): - if request.method == 'DELETE': - return jsonify(cert_manager.revoke_certificate(cert_id)) - elif request.method == 'PUT': - return jsonify(cert_manager.renew_certificate(cert_id)) - - -@app.route('/server-config', methods=['GET', 'PUT']) -@auth.login_required -def server_configuration(): - if request.method == 'GET': - return jsonify(server_config.get_config()) - elif request.method == 'PUT': - new_config = request.json - return jsonify(server_config.update_config(new_config)) - - -@app.route('/logs') -@auth.login_required -def logs(): - filter_params = request.args.to_dict() - return jsonify(log_handler.get_logs(filter_params)) - - -@app.route('/command-history') -@auth.login_required -def command_history(): - return jsonify(log_handler.get_command_history()) - - -def init_app(): - # Any additional initialization can go here - return app - - -if __name__ == '__main__': - app = init_app() - app.run(debug=True) diff --git a/app/server_config.py b/app/server_config.py deleted file mode 100644 index 980e7f0..0000000 --- a/app/server_config.py +++ /dev/null @@ -1,19 +0,0 @@ -from step_ca_shared.cli_wrapper import CLIWrapper -import json - - -class ServerConfig: - def __init__(self): - self.cli = CLIWrapper() - - def get_config(self): - result = self.cli.run_step_ca_command(["ca", "config"]) - if result["status"] == "success": - return json.loads(result["output"]) - return result - - def update_config(self, new_config): - # This is a placeholder. In reality, updating the CA config would be more complex - # and might require restarting the CA service. - result = self.cli.run_step_ca_command(["ca", "config", "update", json.dumps(new_config)]) - return result