diff --git a/src/Server.py b/src/Server.py index 8307880..98275d1 100644 --- a/src/Server.py +++ b/src/Server.py @@ -1188,11 +1188,13 @@ def export_license_request_lic(config: Dict[str, Any]) -> Tuple[Dict[str, Any], ) -def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: +def get_active_licensed_features(db_path: Optional[str] = None) -> List[str]: """ - Checks whether a specific enterprise capability (e.g. 'enterprise_db') is unlocked - either via custom X.509 certificate extensions or the persistent license configuration. + Returns the list of all active enterprise capability identifiers + authorized either via X.509 certificate extensions or the persistent database license config. """ + features = set() + # 1. Check in-memory / active Subordinate CA certificate ca_pem = SERVER_STATE.get("ca_cert_pem") if not ca_pem: @@ -1208,9 +1210,9 @@ def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: if ca_pem: try: info = enrollment.extract_license_info_from_cert(ca_pem) - cert_features = info.get("features", []) - if feature in cert_features: - return True + for feat in info.get("features", []): + if feat: + features.add(str(feat).strip()) except Exception: pass @@ -1230,14 +1232,24 @@ def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: feat_list = json.loads(feat_raw) else: feat_list = [f.strip() for f in feat_raw.split(",") if f.strip()] - if feature in feat_list: - return True - elif isinstance(feat_raw, list) and feature in feat_raw: - return True + for f_item in feat_list: + if f_item: + features.add(str(f_item).strip()) + elif isinstance(feat_raw, list): + for f_item in feat_raw: + if f_item: + features.add(str(f_item).strip()) except Exception: pass - return False + return sorted(list(features)) + + +def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: + """Checks whether a specific enterprise capability (e.g. 'enterprise_db') is authorized.""" + if not feature: + return False + return feature in get_active_licensed_features(db_path) def get_server_public_key() -> Optional[pgpy.PGPKey]: @@ -2863,7 +2875,8 @@ def get_license_summary() -> Dict[str, Any]: "days_remaining": info.get("days_remaining", 0), "fingerprint": info.get("fingerprint", "N/A"), "csr_exists": csr_exists, - "server_fingerprint": config.get("server_fingerprint", "N/A") + "server_fingerprint": config.get("server_fingerprint", "N/A"), + "features": get_active_licensed_features(db_path) } @@ -3396,6 +3409,55 @@ def serve_webui(request: Request = None): pg_disabled_attr = "" if is_db_unlocked else "disabled" pg_label_style = "" if is_db_unlocked else "opacity: 0.55; cursor: not-allowed;" + active_features = summary.get("features", []) + feature_cards = [] + if active_features: + for feat in active_features: + if feat == "enterprise_db": + feature_cards.append(""" +
+
🐘
+
+
+ enterprise_db + AUTHORIZED +
+
+ Enterprise Database & High-Throughput Engine: Unlocks external PostgreSQL storage clustering, customizable in-memory RAM buffering with batch flushing, and OpenPGP zero-knowledge data-at-rest encryption. +
+
+
+ """) + else: + esc_feat = html.escape(str(feat)) + feat_title = html.escape(str(feat).replace("_", " ").title()) + feature_cards.append(f""" +
+
✨
+
+
+ {esc_feat} + AUTHORIZED +
+
+ {feat_title}: Advanced capability authorized by your license certificate. +
+
+
+ """) + else: + feature_cards.append(""" +
+
+
No Enterprise Capabilities Active
+
Standard Community Edition is running. External PostgreSQL pooling, RAM queue buffering, and zero-knowledge encryption require an enterprise_db capability package.
+
+ +
+ """) + + features_block_html = "".join(feature_cards) + ent_community_notice = "" if is_db_unlocked else f"""
@@ -3998,6 +4060,21 @@ def serve_webui(request: Request = None):
+ + +
+
+
+ ✨ Licensed Capabilities & Feature Modules +
Features authorized by your subordinate CA license package and upstream authority
+
+ {len(active_features)} ACTIVE FEATURES +
+ +
+ {features_block_html} +
+
{db_tab_pane_html}