mirror of
https://github.com/outbackdingo/step-ca-webui.git
synced 2026-01-27 18:20:22 +00:00
added code diagram, removed old py code
This commit is contained in:
190
README.md
190
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
|
||||
23
app/auth.py
23
app/auth.py
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
71
app/main.py
71
app/main.py
@@ -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/<cert_id>', 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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user