diff --git a/src/Server.py b/src/Server.py index 2effcf2..af29ec9 100644 --- a/src/Server.py +++ b/src/Server.py @@ -866,7 +866,7 @@ def get_ca_csr(): @app.post("/api/license/import") async def import_signed_license(req: Request): """ - Imports and activates a signed CA certificate issued by logar.eibl.tech. + Imports and activates a signed CA certificate or .lic file issued by logar.eibl.tech. Extracts the seat quota from enterprise X.509 OIDs, stores in SQLite, regenerates Server TLS certificate, and dynamically reloads in-flight mTLS context. """ @@ -875,36 +875,25 @@ async def import_signed_license(req: Request): except Exception: raise HTTPException(status_code=400, detail="Invalid JSON body") - ca_cert_pem = body.get("ca_cert") - if not ca_cert_pem and "lic" in body and isinstance(body["lic"], dict): - ca_cert_pem = body["lic"].get("ca_cert") - if not ca_cert_pem: - raw_text = body.get("raw", "") - if "-----BEGIN CERTIFICATE-----" in raw_text: - ca_cert_pem = raw_text + raw_input = body.get("ca_cert") or body.get("lic") or body.get("raw") + if not raw_input: + raise HTTPException(status_code=400, detail="Missing 'lic' or 'ca_cert' payload in request") - if not ca_cert_pem: - raise HTTPException(status_code=400, detail="Missing 'ca_cert' PEM certificate in request") - - info = enrollment.extract_license_info_from_cert(ca_cert_pem) - if not info.get("valid"): - raise HTTPException(status_code=400, detail=f"Invalid CA certificate: {info.get('error')}") + if isinstance(raw_input, dict): + raw_text = json.dumps(raw_input) + else: + raw_text = str(raw_input) config = SERVER_STATE.get("config", {}) cert_dir = config.get("cert_dir", "certs") - ca_path = os.path.join(cert_dir, "ca.crt") - # Backup existing CA if present - if os.path.exists(ca_path): - ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S") - try: - os.replace(ca_path, f"{ca_path}.{ts}.bak") - except Exception: - pass - - with open(ca_path, "w", encoding="utf-8") as f: - f.write(ca_cert_pem) + try: + res = enrollment.import_license_package(raw_text, cert_dir=cert_dir) + except ValueError as ve: + raise HTTPException(status_code=400, detail=str(ve)) + info = res["license_info"] + ca_cert_pem = res["ca_cert_pem"] new_seats = info.get("seats") or config.get("max_seats", 10) config["max_seats"] = new_seats db_path = config.get("db_path", DEFAULT_DB_FILE) @@ -946,14 +935,30 @@ async def import_signed_license(req: Request): @app.post("/api/license/renew") -def trigger_license_renewal(): - """Explicitly triggers automated mTLS renewal with logar.eibl.tech.""" +@app.post("/api/license/sync") +async def trigger_license_renewal(req: Optional[Request] = None): + """ + Explicitly triggers automated mTLS sync and renewal with logar.eibl.tech. + Supports picking up seat quota increases and certificate extensions in-flight. + """ + requested_seats = None + seat_increment = 0 + if req: + try: + body = await req.json() + requested_seats = body.get("requested_seats") + seat_increment = int(body.get("seat_increment", 0)) + except Exception: + pass + config = SERVER_STATE.get("config", {}) cert_dir = config.get("cert_dir", "certs") lic_server = config.get("license_server_url", "https://logar.eibl.tech") success, msg, new_pem = enrollment.renew_ca_cert_mtls( cert_dir=cert_dir, - license_server_url=lic_server + license_server_url=lic_server, + requested_seats=requested_seats, + seat_increment=seat_increment ) if not success or not new_pem: raise HTTPException(status_code=400, detail=msg) @@ -980,7 +985,7 @@ def trigger_license_renewal(): ssl_ctx = SERVER_STATE.get("ssl_ctx") if ssl_ctx: reload_mtls_context(ssl_ctx, cert_dir=cert_dir) - print("[+] In-flight mTLS SSLContext successfully reloaded after manual renewal.") + print(f"[+] In-flight mTLS SSLContext successfully reloaded after renewal/sync (Seats: {info.get('seats')}).") return { "status": "success", @@ -1343,10 +1348,10 @@ def serve_webui(): 📄 View / Copy CA CSR @@ -1388,7 +1393,7 @@ def serve_webui():
- Submit this CSR or the exported .lic file to __ISSUER_URL__ to receive a CA certificate embedding your seat quota.
+ Submit this CSR or the exported .lic file to __ISSUER_URL__ to receive an activated .lic package embedding your seat quota.